mirror of
https://github.com/tmux/tmux.git
synced 2026-09-17 10:34:46 +00:00
Compare commits
440 Commits
layouts
...
floating_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a91d5601e | ||
|
|
63a8af0e15 | ||
|
|
6b9b6c0b0b | ||
|
|
390805d8b9 | ||
|
|
70f37ef815 | ||
|
|
b6fca55447 | ||
|
|
e22c56f02f | ||
|
|
01c86e64b7 | ||
|
|
ca81c36383 | ||
|
|
a9a04c7bc0 | ||
|
|
0a81f4d742 | ||
|
|
0ad5035457 | ||
|
|
d626cef6de | ||
|
|
7713cbebd2 | ||
|
|
baf16f0419 | ||
|
|
9dcc2da348 | ||
|
|
7de5eb1254 | ||
|
|
f50a38de23 | ||
|
|
3ef51a469c | ||
|
|
d5688bf165 | ||
|
|
8dc9e24c74 | ||
|
|
735bbf9da7 | ||
|
|
6ad86ebdf1 | ||
|
|
3d58e04c93 | ||
|
|
9fb67a2cdd | ||
|
|
d9d12c5c43 | ||
|
|
728ad24d4a | ||
|
|
5b37471db7 | ||
|
|
c41e98be3c | ||
|
|
35f429cb6d | ||
|
|
85fc0ea57c | ||
|
|
6f588402c3 | ||
|
|
fada35478b | ||
|
|
bd5d7a8a7a | ||
|
|
8108356978 | ||
|
|
5e32eddb8c | ||
|
|
1818b6ac16 | ||
|
|
e802909de0 | ||
|
|
824ef37f5f | ||
|
|
565db46a19 | ||
|
|
87aa44f4bc | ||
|
|
e9396e59d9 | ||
|
|
002ac977f8 | ||
|
|
724f85d275 | ||
|
|
b3fa1c0b9c | ||
|
|
e69950a9ca | ||
|
|
41b4743eba | ||
|
|
1e9a16a748 | ||
|
|
74a5069c0a | ||
|
|
8d470017bc | ||
|
|
928c7a9771 | ||
|
|
eda7e563d0 | ||
|
|
90fc51542f | ||
|
|
f8674cc993 | ||
|
|
843adbf454 | ||
|
|
abefc3f705 | ||
|
|
dddd7b44c8 | ||
|
|
25c874c489 | ||
|
|
0e418b62d2 | ||
|
|
a97f643bd3 | ||
|
|
78a2145a47 | ||
|
|
dbe50934b1 | ||
|
|
9e0ca948bf | ||
|
|
8e070f1a34 | ||
|
|
543d104f85 | ||
|
|
e06207c914 | ||
|
|
166267c87a | ||
|
|
91e0737b8a | ||
|
|
65a032b205 | ||
|
|
80c7dcdbd7 | ||
|
|
bcd54d12d8 | ||
|
|
726e19a886 | ||
|
|
95719e342d | ||
|
|
132a63d1da | ||
|
|
822f700b15 | ||
|
|
5df51a06f5 | ||
|
|
ad74c5ed7f | ||
|
|
d5284be91d | ||
|
|
c29b41e527 | ||
|
|
4e612612dc | ||
|
|
705fbf92ee | ||
|
|
2dc704abb6 | ||
|
|
6c2ef75681 | ||
|
|
96899dc527 | ||
|
|
b0db912bde | ||
|
|
7cd33ce0dd | ||
|
|
e0dd40f812 | ||
|
|
79f0f97495 | ||
|
|
5e0b0a2aa3 | ||
|
|
e24969b422 | ||
|
|
dd02989e88 | ||
|
|
7383c04205 | ||
|
|
84291b021f | ||
|
|
91e30f4f22 | ||
|
|
5be44a6c03 | ||
|
|
8cc20a5618 | ||
|
|
81f88f8517 | ||
|
|
4eafb27b89 | ||
|
|
4fc4644652 | ||
|
|
c0f213b056 | ||
|
|
a2915cfdef | ||
|
|
4b921f3821 | ||
|
|
575f84ce0f | ||
|
|
1048796003 | ||
|
|
973c4ab569 | ||
|
|
445677dc00 | ||
|
|
da27529d91 | ||
|
|
f59921ce18 | ||
|
|
ec3c65122f | ||
|
|
9fa5e81836 | ||
|
|
4869a5165a | ||
|
|
30e84ff687 | ||
|
|
73fd01c986 | ||
|
|
3729af7438 | ||
|
|
1e5aef279a | ||
|
|
19db019e0c | ||
|
|
666464d7a9 | ||
|
|
391f880d07 | ||
|
|
d0bf3bf06d | ||
|
|
edb027e910 | ||
|
|
dd2fef6673 | ||
|
|
8e79c4a03d | ||
|
|
19d70a85fc | ||
|
|
f5e9bcc1fc | ||
|
|
3631f81236 | ||
|
|
1e6f64d5c9 | ||
|
|
569e9d932f | ||
|
|
6a7f4a1894 | ||
|
|
8c55a388cd | ||
|
|
e7f414416f | ||
|
|
6ec0afe5f7 | ||
|
|
41e731fc51 | ||
|
|
19c8da04d0 | ||
|
|
8925000c42 | ||
|
|
568c99be4e | ||
|
|
69fc42afc9 | ||
|
|
19843cd826 | ||
|
|
4eb96fc144 | ||
|
|
3d396f13ed | ||
|
|
7d8401d707 | ||
|
|
c3aac57dda | ||
|
|
5602571c01 | ||
|
|
c04236052c | ||
|
|
584cafa284 | ||
|
|
8a4a150a1a | ||
|
|
de41b93b73 | ||
|
|
4aa1dd3ae8 | ||
|
|
be33d01663 | ||
|
|
fdcb93a467 | ||
|
|
3b8e25fef4 | ||
|
|
64e83caf04 | ||
|
|
c54f7ec95e | ||
|
|
f8448764e2 | ||
|
|
762e503978 | ||
|
|
c809aea278 | ||
|
|
1596f9c9f5 | ||
|
|
2f28d9a168 | ||
|
|
baac29debc | ||
|
|
2c4df765c5 | ||
|
|
f71e81c483 | ||
|
|
9bd53b8565 | ||
|
|
dde9dda1a4 | ||
|
|
55c8b0c2a7 | ||
|
|
2d9afa6348 | ||
|
|
bae5c14941 | ||
|
|
9cf8de099c | ||
|
|
f98aaf544d | ||
|
|
78afc24736 | ||
|
|
83e4534387 | ||
|
|
fd1fca51fa | ||
|
|
00620b57eb | ||
|
|
bba9f38636 | ||
|
|
9c37acd002 | ||
|
|
195a9b357a | ||
|
|
cd4c4bf723 | ||
|
|
e244be2540 | ||
|
|
5dad31b06e | ||
|
|
64ded18960 | ||
|
|
4dd496790a | ||
|
|
db9e782d50 | ||
|
|
dabaae1534 | ||
|
|
d4869ebdc5 | ||
|
|
d1fe0d449b | ||
|
|
e2b52abd47 | ||
|
|
f58eb4dc0e | ||
|
|
8497b588a2 | ||
|
|
57641f67dd | ||
|
|
b675c2149c | ||
|
|
4fbc30267c | ||
|
|
2a830aa567 | ||
|
|
84229d4735 | ||
|
|
e370ce5a00 | ||
|
|
2a1ad05671 | ||
|
|
7568bff8e6 | ||
|
|
6766c8ec1d | ||
|
|
2955c1fdc7 | ||
|
|
45e9e541a4 | ||
|
|
cc87db74c7 | ||
|
|
aa1f0653e9 | ||
|
|
8771b6051f | ||
|
|
b2ac4475cc | ||
|
|
a7952492c1 | ||
|
|
bbd4768bb6 | ||
|
|
0b1089e105 | ||
|
|
6b3bdaff97 | ||
|
|
7c908bca7a | ||
|
|
90ede52728 | ||
|
|
17974ea7a0 | ||
|
|
25852623f0 | ||
|
|
446aa08286 | ||
|
|
12b3cc3750 | ||
|
|
6dd288d459 | ||
|
|
d8547fc721 | ||
|
|
68fdc4fcbe | ||
|
|
398cb6fb4a | ||
|
|
7302549903 | ||
|
|
e971bc79d6 | ||
|
|
650ad28f12 | ||
|
|
2637477eea | ||
|
|
e5c69afa98 | ||
|
|
ec901e9e31 | ||
|
|
d814529285 | ||
|
|
82dc3d57f2 | ||
|
|
3f77cb95c5 | ||
|
|
68e9ff797c | ||
|
|
97ee6e2587 | ||
|
|
e0215480cd | ||
|
|
bd33d9c47d | ||
|
|
3a025d39db | ||
|
|
1ed454d102 | ||
|
|
52250d518d | ||
|
|
bfa2ff2bdf | ||
|
|
c62b27f014 | ||
|
|
20cf0d3ea9 | ||
|
|
02caf7ce4a | ||
|
|
e1c7367a2e | ||
|
|
fc4641c5a5 | ||
|
|
8f9e2c7db2 | ||
|
|
46be03d88e | ||
|
|
f9bd5eb79e | ||
|
|
d89a4ab3df | ||
|
|
57b18d8a18 | ||
|
|
95465ca03a | ||
|
|
6513808720 | ||
|
|
6c1773542d | ||
|
|
c60422241b | ||
|
|
92dcb22e92 | ||
|
|
f75b1e5f07 | ||
|
|
fb9e47d8c9 | ||
|
|
43ae6e0a6b | ||
|
|
724d6d7c95 | ||
|
|
95c20aef20 | ||
|
|
791f77b879 | ||
|
|
a6986864db | ||
|
|
30866d06d5 | ||
|
|
8b474429b7 | ||
|
|
91b4e02805 | ||
|
|
e65e653ea5 | ||
|
|
29a1a1f8b0 | ||
|
|
71de737ee8 | ||
|
|
55fbacb469 | ||
|
|
2a6aac4c14 | ||
|
|
4fd8431ad0 | ||
|
|
d3e8dd3623 | ||
|
|
1fa6ebacf4 | ||
|
|
8fa822207d | ||
|
|
52ad3c8425 | ||
|
|
e5abcd217c | ||
|
|
4a0118aea1 | ||
|
|
90a748f7fb | ||
|
|
2434653867 | ||
|
|
1def587884 | ||
|
|
c56bc9ed05 | ||
|
|
2a9ba51907 | ||
|
|
406ae3d8a6 | ||
|
|
8a674265f3 | ||
|
|
ed43033c39 | ||
|
|
0a7b008b21 | ||
|
|
f8e908b89c | ||
|
|
2695c5305e | ||
|
|
55fdfef9d1 | ||
|
|
2f6da393e2 | ||
|
|
57b17bbb70 | ||
|
|
7c23da598d | ||
|
|
b7cc218a03 | ||
|
|
0ae8019e7f | ||
|
|
c3fc0229e5 | ||
|
|
76196b8ffa | ||
|
|
fd4e683151 | ||
|
|
2b05d8531d | ||
|
|
8e966688a7 | ||
|
|
c4ec9234e9 | ||
|
|
2e0eabbf0a | ||
|
|
1b2435fab6 | ||
|
|
1ff3dafe4e | ||
|
|
6b16e93f1b | ||
|
|
b7467a3110 | ||
|
|
4224ecd2ab | ||
|
|
e18c10d340 | ||
|
|
0719dcf2e7 | ||
|
|
3400a312dd | ||
|
|
b2f9144e10 | ||
|
|
a74f173902 | ||
|
|
a1cd68e3f8 | ||
|
|
eec2c19ad1 | ||
|
|
05fdd04262 | ||
|
|
3e36ddbceb | ||
|
|
0a55e5ca54 | ||
|
|
a7ad0c4434 | ||
|
|
271bac5347 | ||
|
|
914ffc8887 | ||
|
|
7a4e35e317 | ||
|
|
1bc85cb59e | ||
|
|
f992c68fd8 | ||
|
|
4bb7a86753 | ||
|
|
950194fb63 | ||
|
|
b528fc438c | ||
|
|
811604a663 | ||
|
|
4232bf9e2f | ||
|
|
8a3620e438 | ||
|
|
1408861511 | ||
|
|
44229e8af6 | ||
|
|
d002323de2 | ||
|
|
999c7246c3 | ||
|
|
c10a597bf0 | ||
|
|
75ed7b27c6 | ||
|
|
ef01e9daf8 | ||
|
|
866cb2d16f | ||
|
|
6a62d49720 | ||
|
|
f201d246fd | ||
|
|
0328fe44e4 | ||
|
|
bb85983a29 | ||
|
|
4341d6a095 | ||
|
|
10a9ce1ed2 | ||
|
|
943490cfa1 | ||
|
|
7e6bbc63ab | ||
|
|
0d195698f8 | ||
|
|
08fd890a89 | ||
|
|
ab0081294c | ||
|
|
c42a939e98 | ||
|
|
6c9e2f7e6c | ||
|
|
992f04f328 | ||
|
|
8a90fce476 | ||
|
|
e928e80a42 | ||
|
|
28c04b21f6 | ||
|
|
46427f1dd7 | ||
|
|
528e856566 | ||
|
|
14a934d412 | ||
|
|
0faed7aeb5 | ||
|
|
8e6450c8fc | ||
|
|
6898e26a7a | ||
|
|
e2cf40ef99 | ||
|
|
96515c9f47 | ||
|
|
96e18faa93 | ||
|
|
476c6e89ad | ||
|
|
b46a96d454 | ||
|
|
f67cf7d053 | ||
|
|
2ad0f4a9e7 | ||
|
|
1ec9ca497b | ||
|
|
dd5d0f4f0c | ||
|
|
d1a6ce8e7f | ||
|
|
b22537e8a4 | ||
|
|
a25c14d472 | ||
|
|
7730d38339 | ||
|
|
58e498c9d3 | ||
|
|
35485f2b5e | ||
|
|
b108653f02 | ||
|
|
25f72cf240 | ||
|
|
d1df8dcccc | ||
|
|
a50d00aa47 | ||
|
|
34add944f2 | ||
|
|
95f85efc49 | ||
|
|
f2f6a05e2c | ||
|
|
bd442a27ee | ||
|
|
73c0eb96c3 | ||
|
|
6a4a4a432b | ||
|
|
263529e886 | ||
|
|
818797ff08 | ||
|
|
d263dbe9dd | ||
|
|
67319ad9d9 | ||
|
|
2591df66cc | ||
|
|
2ac78bccb5 | ||
|
|
47de44001e | ||
|
|
eaa467618b | ||
|
|
ac01f15bfe | ||
|
|
5df5217e2e | ||
|
|
a6ec2b76c2 | ||
|
|
bc01b300c4 | ||
|
|
c8d4d8cb66 | ||
|
|
315202b007 | ||
|
|
7194fdca38 | ||
|
|
31d0945059 | ||
|
|
466e79d572 | ||
|
|
3bb4f72a4b | ||
|
|
329e9d54ab | ||
|
|
9c45dd693b | ||
|
|
cc671e4495 | ||
|
|
61ca158de1 | ||
|
|
7e0038c691 | ||
|
|
04792d0656 | ||
|
|
6c5cc40039 | ||
|
|
6b462474f0 | ||
|
|
9a061a2fee | ||
|
|
6dd552d689 | ||
|
|
34e858ea05 | ||
|
|
39d2839e37 | ||
|
|
7980d00e8c | ||
|
|
25ce5b4281 | ||
|
|
0cd3ab6299 | ||
|
|
28d038fb16 | ||
|
|
10dc308e6b | ||
|
|
6344bab6cc | ||
|
|
984fbacccf | ||
|
|
cc83ca6628 | ||
|
|
04fc6ea11b | ||
|
|
8a9e2fccbd | ||
|
|
cc1324e2d9 | ||
|
|
b2226d1608 | ||
|
|
8ce0af3fc9 | ||
|
|
b315a6c3d1 | ||
|
|
baf642b7d2 | ||
|
|
8db76e9057 | ||
|
|
e3b7bf9b31 | ||
|
|
38724f2e86 | ||
|
|
65fc09fac9 | ||
|
|
3197b715b3 | ||
|
|
26362dfc72 | ||
|
|
275781ce45 | ||
|
|
72dbbfedce | ||
|
|
a6c6eabb5a | ||
|
|
379e4d976c | ||
|
|
a6514e2656 | ||
|
|
032af23e18 | ||
|
|
b5f9293014 | ||
|
|
a37db55689 | ||
|
|
1b959d0eff | ||
|
|
ce03f1abea | ||
|
|
5faf41b695 | ||
|
|
7634daa834 | ||
|
|
968f439672 |
75
.github/workflows/regress.yml
vendored
Normal file
75
.github/workflows/regress.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
name: 'Run Tests'
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '33 3 * * *'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: tmux-tests
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
regress:
|
||||||
|
name: ${{ matrix.name }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
timeout-minutes: 45
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: ubuntu-24.04-x64
|
||||||
|
runner: ubuntu-24.04
|
||||||
|
make: make
|
||||||
|
# - name: ubuntu-24.04-arm64
|
||||||
|
# runner: ubuntu-24.04-arm
|
||||||
|
# make: make
|
||||||
|
# - name: macos-26-arm64
|
||||||
|
# runner: macos-26
|
||||||
|
# make: gmake
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: dependencies
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
autoconf \
|
||||||
|
automake \
|
||||||
|
bison \
|
||||||
|
build-essential \
|
||||||
|
libevent-dev \
|
||||||
|
libncurses-dev \
|
||||||
|
libutf8proc-dev \
|
||||||
|
pkg-config
|
||||||
|
|
||||||
|
- name: dependencies
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
brew install \
|
||||||
|
autoconf \
|
||||||
|
automake \
|
||||||
|
bison \
|
||||||
|
libevent \
|
||||||
|
make \
|
||||||
|
ncurses \
|
||||||
|
utf8proc \
|
||||||
|
pkg-config
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
sh autogen.sh
|
||||||
|
./configure --enable-utf8proc
|
||||||
|
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd regress
|
||||||
|
${{ matrix.make }}
|
||||||
14
CHANGES
14
CHANGES
@@ -1,3 +1,16 @@
|
|||||||
|
CHANGES FROM 3.7a TO 3.7b
|
||||||
|
|
||||||
|
* Fix so that the end of a synchronized update again triggers a redraw.
|
||||||
|
|
||||||
|
CHANGES FROM 3.7 TO 3.7a
|
||||||
|
|
||||||
|
* Fix crash in break-pane when no name is provided.
|
||||||
|
|
||||||
|
* Scrollbar options are now cached rather than being looked up for every redraw
|
||||||
|
(issue 5298).
|
||||||
|
|
||||||
|
* Only forbid #( in names, allow #[, empty names, : and .
|
||||||
|
|
||||||
CHANGES FROM 3.6b TO 3.7
|
CHANGES FROM 3.6b TO 3.7
|
||||||
|
|
||||||
* Add floating panes. These are panes which sit above the layout ("tiled
|
* Add floating panes. These are panes which sit above the layout ("tiled
|
||||||
@@ -5,6 +18,7 @@ CHANGES FROM 3.6b TO 3.7
|
|||||||
the same escape sequence support). Floating panes are created with the
|
the same escape sequence support). Floating panes are created with the
|
||||||
new-pane command, bound to * by default.
|
new-pane command, bound to * by default.
|
||||||
|
|
||||||
|
This is an early release of this feature and they are relatively limited.
|
||||||
Currently floating panes can only be moved and resized using the mouse. The
|
Currently floating panes can only be moved and resized using the mouse. The
|
||||||
default second status line (if status-format is set to 2) has changed to show
|
default second status line (if status-format is set to 2) has changed to show
|
||||||
a list of panes. Many obvious features are not yet available for floating
|
a list of panes. Many obvious features are not yet available for floating
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ dist_tmux_SOURCES = \
|
|||||||
cmd-display-panes.c \
|
cmd-display-panes.c \
|
||||||
cmd-find-window.c \
|
cmd-find-window.c \
|
||||||
cmd-find.c \
|
cmd-find.c \
|
||||||
|
cmd-hide-pane.c \
|
||||||
cmd-if-shell.c \
|
cmd-if-shell.c \
|
||||||
cmd-join-pane.c \
|
cmd-join-pane.c \
|
||||||
cmd-kill-pane.c \
|
cmd-kill-pane.c \
|
||||||
@@ -162,6 +163,7 @@ dist_tmux_SOURCES = \
|
|||||||
file.c \
|
file.c \
|
||||||
format.c \
|
format.c \
|
||||||
format-draw.c \
|
format-draw.c \
|
||||||
|
fuzzy.c \
|
||||||
grid-reader.c \
|
grid-reader.c \
|
||||||
grid-view.c \
|
grid-view.c \
|
||||||
grid.c \
|
grid.c \
|
||||||
@@ -217,6 +219,7 @@ dist_tmux_SOURCES = \
|
|||||||
window-clock.c \
|
window-clock.c \
|
||||||
window-copy.c \
|
window-copy.c \
|
||||||
window-customize.c \
|
window-customize.c \
|
||||||
|
window-switch.c \
|
||||||
window-tree.c \
|
window-tree.c \
|
||||||
window-visible.c \
|
window-visible.c \
|
||||||
window.c \
|
window.c \
|
||||||
|
|||||||
14
arguments.c
14
arguments.c
@@ -149,7 +149,7 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
|
|||||||
int optional_argument)
|
int optional_argument)
|
||||||
{
|
{
|
||||||
struct args_value *argument, *new;
|
struct args_value *argument, *new;
|
||||||
const char *s;
|
const char *s, *as;
|
||||||
|
|
||||||
new = xcalloc(1, sizeof *new);
|
new = xcalloc(1, sizeof *new);
|
||||||
if (*string != '\0') {
|
if (*string != '\0') {
|
||||||
@@ -180,12 +180,24 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
|
|||||||
xasprintf(cause, "-%c expects an argument", flag);
|
xasprintf(cause, "-%c expects an argument", flag);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (optional_argument && argument->type == ARGS_STRING) {
|
||||||
|
as = argument->string;
|
||||||
|
if (as[0] == '-' && (as[1] == '-' || isalpha((u_char)as[1]))) {
|
||||||
|
args_free_value(new);
|
||||||
|
free(new);
|
||||||
|
log_debug("%s: -%c (optional)", __func__, flag);
|
||||||
|
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
args_copy_value(new, argument);
|
args_copy_value(new, argument);
|
||||||
(*i)++;
|
(*i)++;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
s = args_value_as_string(new);
|
s = args_value_as_string(new);
|
||||||
log_debug("%s: -%c = %s", __func__, flag, s);
|
log_debug("%s: -%c = %s", __func__, flag, s);
|
||||||
|
|
||||||
args_set(args, flag, new, 0);
|
args_set(args, flag, new, 0);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ attributes_tostring(int attr)
|
|||||||
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
|
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
|
||||||
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
|
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
|
||||||
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
|
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
|
||||||
(attr & GRID_ATTR_OVERLINE) ? "overline," : "",
|
(attr & GRID_ATTR_OVERLINE) ? "overline," : "",
|
||||||
(attr & GRID_ATTR_NOATTR) ? "noattr," : "");
|
(attr & GRID_ATTR_NOATTR) ? "noattr," : "");
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
buf[len - 1] = '\0';
|
buf[len - 1] = '\0';
|
||||||
|
|||||||
@@ -34,9 +34,10 @@ const struct cmd_entry cmd_break_pane_entry = {
|
|||||||
.name = "break-pane",
|
.name = "break-pane",
|
||||||
.alias = "breakp",
|
.alias = "breakp",
|
||||||
|
|
||||||
.args = { "abdPF:n:s:t:", 0, 0, NULL },
|
.args = { "abdPF:n:s:t:Wx:X:y:Y:", 0, 0, NULL },
|
||||||
.usage = "[-abdP] [-F format] [-n window-name] [-s src-pane] "
|
.usage = "[-abdPW] [-F format] [-n window-name] [-s src-pane] "
|
||||||
"[-t dst-window]",
|
"[-t dst-window] [-x width] [-y height] [-X x-position] "
|
||||||
|
"[-Y y-position]",
|
||||||
|
|
||||||
.source = { 's', CMD_FIND_PANE, 0 },
|
.source = { 's', CMD_FIND_PANE, 0 },
|
||||||
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
|
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
|
||||||
@@ -45,6 +46,52 @@ const struct cmd_entry cmd_break_pane_entry = {
|
|||||||
.exec = cmd_break_pane_exec
|
.exec = cmd_break_pane_exec
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_break_pane_float(struct cmdq_item *item, struct args *args,
|
||||||
|
struct window *w, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct layout_cell *lc = wp->layout_cell;
|
||||||
|
u_int sx = lc->saved_sx, sy = lc->saved_sy;
|
||||||
|
int ox = lc->saved_xoff, oy = lc->saved_yoff;
|
||||||
|
char *cause = NULL;
|
||||||
|
enum pane_lines lines = window_get_pane_lines(w);
|
||||||
|
|
||||||
|
if (window_pane_is_floating(wp)) {
|
||||||
|
cmdq_error(item, "pane is already floating");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if (window_pane_is_hidden(wp)) {
|
||||||
|
cmdq_error(item, "can't float a hidden pane");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if (w->flags & WINDOW_ZOOMED) {
|
||||||
|
cmdq_error(item, "can't float a pane while window is zoomed");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
|
||||||
|
&cause) != 0) {
|
||||||
|
cmdq_error(item, "failed to float pane: %s", cause);
|
||||||
|
free(cause);
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
layout_remove_tile(w, lc);
|
||||||
|
layout_set_size(lc, sx, sy, ox, oy);
|
||||||
|
|
||||||
|
lc->flags |= LAYOUT_CELL_FLOATING;
|
||||||
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
|
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
||||||
|
|
||||||
|
if (!args_has(args, 'd'))
|
||||||
|
window_set_active_pane(w, wp, 1);
|
||||||
|
layout_fix_offsets(w);
|
||||||
|
layout_fix_panes(w, NULL);
|
||||||
|
notify_window("window-layout-changed", w);
|
||||||
|
server_redraw_window(w);
|
||||||
|
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
static enum cmd_retval
|
static enum cmd_retval
|
||||||
cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
@@ -62,7 +109,10 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
int idx = target->idx, before;
|
int idx = target->idx, before;
|
||||||
const char *template, *name = args_get(args, 'n');
|
const char *template, *name = args_get(args, 'n');
|
||||||
|
|
||||||
if (name != NULL && !check_name(name, WINDOW_NAME_FORBID)) {
|
if (args_has(args, 'W'))
|
||||||
|
return (cmd_break_pane_float(item, args, w, wp));
|
||||||
|
|
||||||
|
if (name != NULL && !check_name(name)) {
|
||||||
cmdq_error(item, "invalid window name: %s", name);
|
cmdq_error(item, "invalid window name: %s", name);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
@@ -86,7 +136,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
window_set_name(w, name, WINDOW_NAME_FORBID);
|
window_set_name(w, name, 0);
|
||||||
options_set_number(w->options, "automatic-rename", 0);
|
options_set_number(w->options, "automatic-rename", 0);
|
||||||
}
|
}
|
||||||
server_unlink_window(src_s, wl);
|
server_unlink_window(src_s, wl);
|
||||||
@@ -116,7 +166,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
newname = default_window_name(w);
|
newname = default_window_name(w);
|
||||||
window_set_name(w, newname, WINDOW_NAME_FORBID);
|
window_set_name(w, newname, 0);
|
||||||
free(newname);
|
free(newname);
|
||||||
} else {
|
} else {
|
||||||
window_set_name(w, name, 0);
|
window_set_name(w, name, 0);
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
|
|||||||
.name = "capture-pane",
|
.name = "capture-pane",
|
||||||
.alias = "capturep",
|
.alias = "capturep",
|
||||||
|
|
||||||
.args = { "ab:CeE:FHJLMNpPqS:Tt:", 0, 0, NULL },
|
.args = { "ab:CeE:FHJLMNpPqRS:Tt:", 0, 0, NULL },
|
||||||
.usage = "[-aCeFHJLMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
|
.usage = "[-aCeFHJLMNpPqRT] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||||
"[-S start-line] " CMD_TARGET_PANE_USAGE,
|
"[-S start-line] " CMD_TARGET_PANE_USAGE,
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
@@ -75,6 +75,96 @@ cmd_capture_pane_append(char *buf, size_t *len, const char *line,
|
|||||||
return (buf);
|
return (buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
cmd_capture_pane_cell(struct screen *s, u_int xx, u_int yy)
|
||||||
|
{
|
||||||
|
struct grid *gd = s->grid;
|
||||||
|
struct hyperlinks *hl = s->hyperlinks;
|
||||||
|
struct grid_cell gc;
|
||||||
|
char *line, *data, *link, *linkid, *f, *b, *u;
|
||||||
|
char c[UTF8_SIZE + 1];
|
||||||
|
const char *uri, *iid;
|
||||||
|
u_int flags;
|
||||||
|
|
||||||
|
grid_get_cell(gd, xx, yy, &gc);
|
||||||
|
|
||||||
|
memcpy(c, gc.data.data, gc.data.size);
|
||||||
|
c[gc.data.size] = '\0';
|
||||||
|
utf8_stravis(&data, c, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
|
||||||
|
|
||||||
|
if (gc.link != 0 && hyperlinks_get(hl, gc.link, &uri, &iid, NULL)) {
|
||||||
|
xasprintf(&link, "%s", uri);
|
||||||
|
if (iid != NULL && *iid != '\0')
|
||||||
|
xasprintf(&linkid, "%s", iid);
|
||||||
|
else
|
||||||
|
xasprintf(&linkid, "NONE");
|
||||||
|
} else {
|
||||||
|
xasprintf(&link, "NONE");
|
||||||
|
xasprintf(&linkid, "NONE");
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = gc.flags;
|
||||||
|
if (gc.fg & COLOUR_FLAG_256)
|
||||||
|
flags |= GRID_FLAG_FG256;
|
||||||
|
if (gc.bg & COLOUR_FLAG_256)
|
||||||
|
flags |= GRID_FLAG_BG256;
|
||||||
|
|
||||||
|
xasprintf(&f, "%s[%x]", colour_tostring(gc.fg), gc.fg);
|
||||||
|
xasprintf(&b, "%s[%x]", colour_tostring(gc.bg), gc.bg);
|
||||||
|
xasprintf(&u, "%s[%x]", colour_tostring(gc.us), gc.us);
|
||||||
|
|
||||||
|
xasprintf(&line, "\t\tC %u,%u data=(%u,%u,%s) flags=%s[%x] "
|
||||||
|
"attr=%s[%x] fg=%s bg=%s us=%s link=%s linkid=%s\n",
|
||||||
|
yy, xx, gc.data.width, gc.data.size, data,
|
||||||
|
grid_cell_flags_string(flags), flags,
|
||||||
|
grid_cell_attr_string(gc.attr), gc.attr, f, b, u, link, linkid);
|
||||||
|
|
||||||
|
free(f);
|
||||||
|
free(b);
|
||||||
|
free(u);
|
||||||
|
free(link);
|
||||||
|
free(linkid);
|
||||||
|
free(data);
|
||||||
|
return (line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
|
||||||
|
{
|
||||||
|
struct screen *s = &wp->base;
|
||||||
|
struct grid *gd = s->grid;
|
||||||
|
struct grid_line *gl;
|
||||||
|
char *buf = xstrdup(""), *line;
|
||||||
|
char p[11];
|
||||||
|
u_int yy, xx, total = gd->hsize + gd->sy;
|
||||||
|
|
||||||
|
xasprintf(&line, "G %ux%u (%u/%u)\n", gd->sx, gd->sy, gd->hsize,
|
||||||
|
gd->hlimit);
|
||||||
|
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
for (yy = 0; yy < total; yy++) {
|
||||||
|
gl = grid_get_line(gd, yy);
|
||||||
|
if (yy < gd->hsize)
|
||||||
|
snprintf(p, sizeof p, "-");
|
||||||
|
else
|
||||||
|
snprintf(p, sizeof p, "%u", yy - gd->hsize);
|
||||||
|
xasprintf(&line, "\tL %u (%s) flags=%s[%x] %u/%u\n", yy,
|
||||||
|
p, grid_line_flags_string(gl->flags), gl->flags,
|
||||||
|
gl->cellused, gl->cellsize);
|
||||||
|
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
for (xx = 0; xx < gd->sx; xx++) {
|
||||||
|
line = cmd_capture_pane_cell(s, xx, yy);
|
||||||
|
buf = cmd_capture_pane_append(buf, len, line,
|
||||||
|
strlen(line));
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
|
cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
|
||||||
size_t *len)
|
size_t *len)
|
||||||
@@ -323,7 +413,9 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
len = 0;
|
len = 0;
|
||||||
if (args_has(args, 'P') && !args_has(args, 'H'))
|
if (args_has(args, 'R'))
|
||||||
|
buf = cmd_capture_pane_grid(wp, &len);
|
||||||
|
else if (args_has(args, 'P') && !args_has(args, 'H'))
|
||||||
buf = cmd_capture_pane_pending(args, wp, &len);
|
buf = cmd_capture_pane_pending(args, wp, &len);
|
||||||
else
|
else
|
||||||
buf = cmd_capture_pane_history(args, item, wp, &len);
|
buf = cmd_capture_pane_history(args, item, wp, &len);
|
||||||
|
|||||||
@@ -84,6 +84,19 @@ const struct cmd_entry cmd_customize_mode_entry = {
|
|||||||
.exec = cmd_choose_tree_exec
|
.exec = cmd_choose_tree_exec
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const struct cmd_entry cmd_switch_mode_entry = {
|
||||||
|
.name = "switch-mode",
|
||||||
|
.alias = NULL,
|
||||||
|
|
||||||
|
.args = { "F:kst:wZ", 0, 1, cmd_choose_tree_args_parse },
|
||||||
|
.usage = "[-kswZ] [-F format] " CMD_TARGET_PANE_USAGE " [command]",
|
||||||
|
|
||||||
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
|
.flags = 0,
|
||||||
|
.exec = cmd_choose_tree_exec
|
||||||
|
};
|
||||||
|
|
||||||
static enum args_parse_type
|
static enum args_parse_type
|
||||||
cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx,
|
cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx,
|
||||||
__unused char **cause)
|
__unused char **cause)
|
||||||
@@ -116,6 +129,8 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
mode = &window_client_mode;
|
mode = &window_client_mode;
|
||||||
} else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
|
} else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
|
||||||
mode = &window_customize_mode;
|
mode = &window_customize_mode;
|
||||||
|
else if (cmd_get_entry(self) == &cmd_switch_mode_entry)
|
||||||
|
mode = &window_switch_mode;
|
||||||
else
|
else
|
||||||
mode = &window_tree_mode;
|
mode = &window_tree_mode;
|
||||||
|
|
||||||
|
|||||||
11
cmd-find.c
11
cmd-find.c
@@ -994,15 +994,20 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
|||||||
} else if (cmd_find_from_client(¤t, cmdq_get_client(item),
|
} else if (cmd_find_from_client(¤t, cmdq_get_client(item),
|
||||||
flags) == 0) {
|
flags) == 0) {
|
||||||
fs->current = ¤t;
|
fs->current = ¤t;
|
||||||
|
/* No active pane, window empty, return the window instead. */
|
||||||
|
if (current.wp == NULL) {
|
||||||
|
type = CMD_FIND_WINDOW;
|
||||||
|
}
|
||||||
log_debug("%s: current is from client", __func__);
|
log_debug("%s: current is from client", __func__);
|
||||||
} else {
|
} else {
|
||||||
if (~flags & CMD_FIND_QUIET)
|
if (~flags & CMD_FIND_QUIET)
|
||||||
cmdq_error(item, "no current target");
|
cmdq_error(item, "no current target");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
if (!cmd_find_valid_state(fs->current))
|
if (!cmd_find_valid_state(fs->current))
|
||||||
fatalx("invalid current find state");
|
fatalx("invalid current find state");
|
||||||
|
*/
|
||||||
/* An empty or NULL target is the current. */
|
/* An empty or NULL target is the current. */
|
||||||
if (target == NULL || *target == '\0')
|
if (target == NULL || *target == '\0')
|
||||||
goto current;
|
goto current;
|
||||||
@@ -1011,7 +1016,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
|||||||
strcmp(target, "{active}") == 0 ||
|
strcmp(target, "{active}") == 0 ||
|
||||||
strcmp(target, "{current}") == 0) {
|
strcmp(target, "{current}") == 0) {
|
||||||
c = cmdq_get_client(item);
|
c = cmdq_get_client(item);
|
||||||
if (c == NULL) {
|
if (c == NULL || c->session == NULL) {
|
||||||
cmdq_error(item, "no current client");
|
cmdq_error(item, "no current client");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -1041,7 +1046,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
|||||||
fs->w = fs->wl->window;
|
fs->w = fs->wl->window;
|
||||||
fs->wp = fs->w->active;
|
fs->wp = fs->w->active;
|
||||||
}
|
}
|
||||||
break;
|
goto found;
|
||||||
}
|
}
|
||||||
if (fs->wp == NULL) {
|
if (fs->wp == NULL) {
|
||||||
if (~flags & CMD_FIND_QUIET)
|
if (~flags & CMD_FIND_QUIET)
|
||||||
|
|||||||
213
cmd-hide-pane.c
Normal file
213
cmd-hide-pane.c
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/* $OpenBSD$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Michael Grant <mgrant@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hide or show panes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static enum cmd_retval cmd_hide_pane_exec(struct cmd *, struct cmdq_item *);
|
||||||
|
static enum cmd_retval cmd_show_pane_exec(struct cmd *, struct cmdq_item *);
|
||||||
|
|
||||||
|
const struct cmd_entry cmd_hide_pane_entry = {
|
||||||
|
.name = "hide-pane",
|
||||||
|
.alias = "hidep",
|
||||||
|
|
||||||
|
.args = { "at:", 0, 1, NULL },
|
||||||
|
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||||
|
|
||||||
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
|
.flags = CMD_AFTERHOOK,
|
||||||
|
.exec = cmd_hide_pane_exec
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct cmd_entry cmd_show_pane_entry = {
|
||||||
|
.name = "show-pane",
|
||||||
|
.alias = "showp",
|
||||||
|
|
||||||
|
.args = { "at:", 0, 1, NULL },
|
||||||
|
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||||
|
|
||||||
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
|
.flags = CMD_AFTERHOOK,
|
||||||
|
.exec = cmd_show_pane_exec
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_hide_pane_hide(struct window *w, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct window_pane *wpp = NULL;
|
||||||
|
struct layout_cell *lc = wp->layout_cell;
|
||||||
|
|
||||||
|
if (window_pane_is_hidden(wp))
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
|
if (wp == w->active) {
|
||||||
|
/* Find previous active pane. */
|
||||||
|
TAILQ_FOREACH(wpp, &w->last_panes, sentry) {
|
||||||
|
if (wpp != wp && window_pane_is_visible(wpp))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (wpp == NULL) {
|
||||||
|
TAILQ_FOREACH(wpp, &w->z_index, zentry) {
|
||||||
|
if (wpp != wp &&
|
||||||
|
window_pane_is_visible(wpp))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lc->flags |= LAYOUT_CELL_HIDDEN;
|
||||||
|
|
||||||
|
layout_remove_tile(w, lc);
|
||||||
|
layout_fix_offsets(w);
|
||||||
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
|
window_pane_stack_remove(&w->last_panes, wp);
|
||||||
|
if (wpp != NULL) {
|
||||||
|
window_set_active_pane(w, wpp, 1);
|
||||||
|
} else if (wp == w->active) {
|
||||||
|
/* No visible previous active pane; null active pane
|
||||||
|
* to show dots background. */
|
||||||
|
w->active = NULL;
|
||||||
|
if (options_get_number(global_options, "focus-events"))
|
||||||
|
window_pane_update_focus(wp);
|
||||||
|
notify_window("window-pane-changed", w);
|
||||||
|
notify_window("window-layout-changed", w);
|
||||||
|
server_redraw_window(w);
|
||||||
|
} else {
|
||||||
|
notify_window("window-layout-changed", w);
|
||||||
|
server_redraw_window(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_hide_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
|
{
|
||||||
|
struct args *args = cmd_get_args(self);
|
||||||
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
|
struct winlink *wl = target->wl;
|
||||||
|
struct window *w = wl->window;
|
||||||
|
struct window_pane *wp, *active_pane = w->active;
|
||||||
|
u_int id;
|
||||||
|
char *cause = NULL;
|
||||||
|
enum cmd_retval rv;
|
||||||
|
|
||||||
|
window_unzoom(w, 1);
|
||||||
|
|
||||||
|
if (args_has(args, 'a')) {
|
||||||
|
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||||
|
if (!window_pane_is_visible(wp) || wp == active_pane)
|
||||||
|
continue;
|
||||||
|
rv = cmd_hide_pane_hide(w, wp);
|
||||||
|
if (rv != CMD_RETURN_NORMAL)
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
} else {
|
||||||
|
wp = target->wp;
|
||||||
|
if (wp == NULL) {
|
||||||
|
id = args_strtonum_and_expand(args, 't', 0, INT_MAX,
|
||||||
|
item, &cause);
|
||||||
|
if (cause != NULL) {
|
||||||
|
cmdq_error(item, "%s target pane", cause);
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
wp = window_pane_find_by_id(id);
|
||||||
|
}
|
||||||
|
if (wp == NULL) {
|
||||||
|
cmdq_error(item, "No target pane to hide.");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
return (cmd_hide_pane_hide(w, wp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_hide_pane_show(struct window *w, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct layout_cell *lc = wp->layout_cell;
|
||||||
|
|
||||||
|
if (!window_pane_is_hidden(wp))
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
|
lc->flags &= ~LAYOUT_CELL_HIDDEN;
|
||||||
|
|
||||||
|
layout_insert_tile(w, lc);
|
||||||
|
layout_fix_offsets(w);
|
||||||
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
|
window_set_active_pane(w, wp, 1);
|
||||||
|
|
||||||
|
notify_window("window-layout-changed", w);
|
||||||
|
server_redraw_window(w);
|
||||||
|
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_show_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
|
{
|
||||||
|
struct args *args = cmd_get_args(self);
|
||||||
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
|
struct winlink *wl = target->wl;
|
||||||
|
struct window *w = wl->window;
|
||||||
|
struct window_pane *wp;
|
||||||
|
u_int id;
|
||||||
|
char *cause = NULL;
|
||||||
|
enum cmd_retval rv;
|
||||||
|
|
||||||
|
window_unzoom(w, 1);
|
||||||
|
|
||||||
|
if (args_has(args, 'a')) {
|
||||||
|
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||||
|
if (!window_pane_is_visible(wp))
|
||||||
|
continue;
|
||||||
|
rv = cmd_hide_pane_show(w, wp);
|
||||||
|
if (rv != CMD_RETURN_NORMAL)
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
} else {
|
||||||
|
wp = target->wp;
|
||||||
|
if (wp == NULL) {
|
||||||
|
id = args_strtonum_and_expand(args, 't', 0, INT_MAX,
|
||||||
|
item, &cause);
|
||||||
|
if (cause != NULL) {
|
||||||
|
cmdq_error(item, "%s target pane", cause);
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
wp = window_pane_find_by_id(id);
|
||||||
|
}
|
||||||
|
if (wp == NULL) {
|
||||||
|
cmdq_error(item, "No target pane to show.");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
return (cmd_hide_pane_show(w, wp));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ const struct cmd_entry cmd_move_pane_entry = {
|
|||||||
.name = "move-pane",
|
.name = "move-pane",
|
||||||
.alias = "movep",
|
.alias = "movep",
|
||||||
|
|
||||||
.args = { "bdfhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL },
|
.args = { "bdD::fhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL },
|
||||||
.usage = "[-bdfhMv] [-D lines] [-l size] [-L columns] [-P position] "
|
.usage = "[-bdfhMv] [-D lines] [-l size] [-L columns] [-P position] "
|
||||||
"[-R columns] " CMD_SRCDST_PANE_USAGE " [-U lines] "
|
"[-R columns] " CMD_SRCDST_PANE_USAGE " [-U lines] "
|
||||||
"[-X x-position] [-Y y-position] [-z z-index]",
|
"[-X x-position] [-Y y-position] [-z z-index]",
|
||||||
@@ -363,6 +363,45 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
|
|||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
|
||||||
|
struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct layout_cell *lc = wp->layout_cell;
|
||||||
|
|
||||||
|
if (!window_pane_is_floating(wp)) {
|
||||||
|
cmdq_error(item, "pane is not floating");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if (window_pane_is_hidden(wp)) {
|
||||||
|
cmdq_error(item, "can't tile a hidden pane");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if (w->flags & WINDOW_ZOOMED) {
|
||||||
|
cmdq_error(item, "can't tile a pane while window is zoomed");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout_save_size(lc);
|
||||||
|
lc->flags &= ~LAYOUT_CELL_FLOATING;
|
||||||
|
if (layout_insert_tile(w, lc) == 0) {
|
||||||
|
cmdq_error(item, "no space for a new pane");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
|
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||||
|
|
||||||
|
if (!args_has(args, 'd'))
|
||||||
|
window_set_active_pane(w, wp, 1);
|
||||||
|
layout_fix_offsets(w);
|
||||||
|
layout_fix_panes(w, NULL);
|
||||||
|
notify_window("window-layout-changed", w);
|
||||||
|
server_redraw_window(w);
|
||||||
|
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
static enum cmd_retval
|
static enum cmd_retval
|
||||||
cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
@@ -377,7 +416,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
const char *s;
|
const char *s;
|
||||||
char *cause = NULL;
|
char *cause = NULL;
|
||||||
int flags = 0, dst_idx;
|
int flags = 0, dst_idx;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc, *src_lc;
|
||||||
|
|
||||||
dst_s = target->s;
|
dst_s = target->s;
|
||||||
dst_wl = target->wl;
|
dst_wl = target->wl;
|
||||||
@@ -408,9 +447,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
src_wl = source->wl;
|
src_wl = source->wl;
|
||||||
src_wp = source->wp;
|
src_wp = source->wp;
|
||||||
|
src_lc = src_wp->layout_cell;
|
||||||
src_w = src_wl->window;
|
src_w = src_wl->window;
|
||||||
server_unzoom_window(src_w);
|
server_unzoom_window(src_w);
|
||||||
|
|
||||||
|
if (!args_has(args, 't') && window_pane_is_floating(src_wp))
|
||||||
|
return (cmd_join_pane_tile(item, args, src_w, src_wp));
|
||||||
|
|
||||||
if (src_wp == dst_wp) {
|
if (src_wp == dst_wp) {
|
||||||
cmdq_error(item, "source and target panes must be different");
|
cmdq_error(item, "source and target panes must be different");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
@@ -422,6 +465,12 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
if (window_pane_is_floating(src_wp)) {
|
||||||
|
lc->saved_sx = src_lc->sx;
|
||||||
|
lc->saved_sy = src_lc->sy;
|
||||||
|
lc->saved_xoff = src_lc->xoff;
|
||||||
|
lc->saved_yoff = src_lc->yoff;
|
||||||
|
}
|
||||||
|
|
||||||
layout_close_pane(src_wp);
|
layout_close_pane(src_wp);
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
n = 1;
|
n = 1;
|
||||||
|
|
||||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||||
format_defaults(ft, NULL, NULL, NULL, NULL);
|
format_defaults(ft, tc, NULL, NULL, NULL);
|
||||||
format_add(ft, "notes_only", "%d", notes_only);
|
format_add(ft, "notes_only", "%d", notes_only);
|
||||||
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
|
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
|
||||||
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
|
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
|
||||||
|
|||||||
@@ -101,22 +101,22 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if ((tmp = args_get(args, 'n')) != NULL) {
|
if ((tmp = args_get(args, 'n')) != NULL) {
|
||||||
ename = format_single(item, tmp, c, NULL, NULL, NULL);
|
ename = format_single(item, tmp, c, NULL, NULL, NULL);
|
||||||
if (!check_name(ename, WINDOW_NAME_FORBID)) {
|
if (!check_name(ename)) {
|
||||||
cmdq_error(item, "invalid window name: %s", ename);
|
cmdq_error(item, "invalid window name: %s", ename);
|
||||||
free(ename);
|
free(ename);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
wname = clean_name(ename, WINDOW_NAME_FORBID);
|
wname = clean_name(ename, 0);
|
||||||
free(ename);
|
free(ename);
|
||||||
}
|
}
|
||||||
if ((tmp = args_get(args, 's')) != NULL) {
|
if ((tmp = args_get(args, 's')) != NULL) {
|
||||||
ename = format_single(item, tmp, c, NULL, NULL, NULL);
|
ename = format_single(item, tmp, c, NULL, NULL, NULL);
|
||||||
if (!check_name(ename, SESSION_NAME_FORBID)) {
|
if (!check_name(ename)) {
|
||||||
cmdq_error(item, "invalid session name: %s", ename);
|
cmdq_error(item, "invalid session name: %s", ename);
|
||||||
free(ename);
|
free(ename);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
sname = clean_name(ename, SESSION_NAME_FORBID);
|
sname = clean_name(ename, 0);
|
||||||
free(ename);
|
free(ename);
|
||||||
}
|
}
|
||||||
if (args_has(args, 'A')) {
|
if (args_has(args, 'A')) {
|
||||||
@@ -152,12 +152,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
else if (groupwith != NULL)
|
else if (groupwith != NULL)
|
||||||
prefix = xstrdup(groupwith->name);
|
prefix = xstrdup(groupwith->name);
|
||||||
else {
|
else {
|
||||||
if (!check_name(group, SESSION_NAME_FORBID)) {
|
if (!check_name(group)) {
|
||||||
cmdq_error(item,
|
cmdq_error(item,
|
||||||
"invalid session group name: %s", group);
|
"invalid session group name: %s", group);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
prefix = clean_name(group, SESSION_NAME_FORBID);
|
prefix = clean_name(group, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,12 +73,12 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
name = args_get(args, 'n');
|
name = args_get(args, 'n');
|
||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
expanded = format_single(item, name, c, s, NULL, NULL);
|
expanded = format_single(item, name, c, s, NULL, NULL);
|
||||||
if (!check_name(expanded, WINDOW_NAME_FORBID)) {
|
if (!check_name(expanded)) {
|
||||||
cmdq_error(item, "invalid window name: %s", expanded);
|
cmdq_error(item, "invalid window name: %s", expanded);
|
||||||
free(expanded);
|
free(expanded);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
wname = clean_name(expanded, WINDOW_NAME_FORBID);
|
wname = clean_name(expanded, 0);
|
||||||
free(expanded);
|
free(expanded);
|
||||||
}
|
}
|
||||||
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {
|
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
char *newname, *tmp;
|
char *newname, *tmp;
|
||||||
|
|
||||||
tmp = format_single_from_target(item, args_string(args, 0));
|
tmp = format_single_from_target(item, args_string(args, 0));
|
||||||
if (!check_name(tmp, SESSION_NAME_FORBID)) {
|
if (!check_name(tmp)) {
|
||||||
cmdq_error(item, "invalid session name: %s", tmp);
|
cmdq_error(item, "invalid session name: %s", tmp);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
newname = clean_name(tmp, SESSION_NAME_FORBID);
|
newname = clean_name(tmp, 0);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
if (strcmp(newname, s->name) == 0) {
|
if (strcmp(newname, s->name) == 0) {
|
||||||
free(newname);
|
free(newname);
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
name = format_single_from_target(item, args_string(args, 0));
|
name = format_single_from_target(item, args_string(args, 0));
|
||||||
if (!check_name(name, WINDOW_NAME_FORBID)) {
|
if (!check_name(name)) {
|
||||||
cmdq_error(item, "invalid window name: %s", name);
|
cmdq_error(item, "invalid window name: %s", name);
|
||||||
free(name);
|
free(name);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
window_set_name(wl->window, name, WINDOW_NAME_FORBID);
|
window_set_name(wl->window, name, 0);
|
||||||
options_set_number(wl->window->options, "automatic-rename", 0);
|
options_set_number(wl->window->options, "automatic-rename", 0);
|
||||||
free(name);
|
free(name);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -63,8 +64,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
const char *errstr, *argval;
|
const char *errstr, *argval;
|
||||||
const char flags[4] = { 'U', 'D', 'L', 'R' };
|
const char flags[4] = { 'U', 'D', 'L', 'R' };
|
||||||
char *cause = NULL, flag;
|
char *cause = NULL, flag;
|
||||||
u_int opposite = 0;
|
int adjust, x, y, status, opposite = 0;
|
||||||
int adjust, x, y, status;
|
|
||||||
long unsigned i;
|
long unsigned i;
|
||||||
struct grid *gd = wp->base.grid;
|
struct grid *gd = wp->base.grid;
|
||||||
|
|
||||||
@@ -101,9 +101,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
if (window_pane_is_floating(wp)) {
|
if (window_pane_is_floating(wp)) {
|
||||||
layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT, x,
|
if (layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT,
|
||||||
&cause);
|
x, &cause) != 0) {
|
||||||
if (cause != NULL) {
|
|
||||||
cmdq_error(item, "size %s", cause);
|
cmdq_error(item, "size %s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
@@ -130,9 +129,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (window_pane_is_floating(wp)) {
|
if (window_pane_is_floating(wp)) {
|
||||||
layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM, y,
|
if (layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM,
|
||||||
&cause);
|
y, &cause) != 0) {
|
||||||
if (cause != NULL) {
|
|
||||||
cmdq_error(item, "size %s", cause);
|
cmdq_error(item, "size %s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
@@ -147,8 +145,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
argval = args_get(args, flag);
|
argval = args_get(args, flag);
|
||||||
if (argval == NULL)
|
if (argval == NULL) {
|
||||||
argval = "1";
|
if (args_count(args) == 0)
|
||||||
|
argval = "1";
|
||||||
|
else
|
||||||
|
argval = args_string(args, 0);
|
||||||
|
}
|
||||||
|
|
||||||
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
|
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
|
||||||
if (errstr != NULL) {
|
if (errstr != NULL) {
|
||||||
@@ -164,9 +166,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (flag == 'L' || flag == 'U')
|
if (flag == 'L' || flag == 'U')
|
||||||
opposite = 1;
|
opposite = 1;
|
||||||
|
|
||||||
layout_resize_floating_pane(wp, type, adjust, opposite,
|
if (layout_resize_floating_pane(wp, type, adjust,
|
||||||
&cause);
|
opposite, &cause) != 0) {
|
||||||
if (cause != NULL) {
|
|
||||||
cmdq_error(item, "adjustment %s", cause);
|
cmdq_error(item, "adjustment %s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
@@ -234,7 +235,7 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
|||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc;
|
||||||
int y, ly, x, lx, sx, sy, new_sx, new_sy;
|
int y, ly, x, lx, sx, sy, new_sx, new_sy;
|
||||||
int scrollbars, sb_pos, left, right;
|
int left, right;
|
||||||
int new_xoff, new_yoff, resizes = 0;
|
int new_xoff, new_yoff, resizes = 0;
|
||||||
|
|
||||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||||
@@ -246,15 +247,13 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
|||||||
lc = wp->layout_cell;
|
lc = wp->layout_cell;
|
||||||
sx = wp->sx;
|
sx = wp->sx;
|
||||||
sy = wp->sy;
|
sy = wp->sy;
|
||||||
scrollbars = options_get_number(w->options, "pane-scrollbars");
|
|
||||||
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
|
|
||||||
left = wp->xoff - 1;
|
left = wp->xoff - 1;
|
||||||
right = wp->xoff + sx;
|
right = wp->xoff + sx;
|
||||||
if (window_pane_show_scrollbar(wp, scrollbars) &&
|
if (window_pane_scrollbar_reserve(wp) &&
|
||||||
sb_pos == PANE_SCROLLBARS_LEFT) {
|
w->sb_pos == PANE_SCROLLBARS_LEFT) {
|
||||||
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
|
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
|
||||||
} else if (window_pane_show_scrollbar(wp, scrollbars) &&
|
} else if (window_pane_scrollbar_reserve(wp) &&
|
||||||
sb_pos == PANE_SCROLLBARS_RIGHT) {
|
w->sb_pos == PANE_SCROLLBARS_RIGHT) {
|
||||||
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
|
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
|
|||||||
|
|
||||||
.args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
.args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||||
.usage = "[-bdefhIklPvWZ] [-B border-lines] "
|
.usage = "[-bdefhIklPvWZ] [-B border-lines] "
|
||||||
"[-c start-directory] [-e environment] "
|
"[-c start-directory] [-e environment] "
|
||||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||||
"[-s style] [-S active-border-style] "
|
"[-s style] [-S active-border-style] "
|
||||||
"[-R inactive-border-style] [-T title] [-x width] [-y height] "
|
"[-R inactive-border-style] [-T title] [-x width] [-y height] "
|
||||||
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
|
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
|
||||||
@@ -95,7 +95,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
||||||
is_floating = !args_has(args, 'L');
|
is_floating = !args_has(args, 'L');
|
||||||
else
|
else
|
||||||
is_floating = 0;
|
is_floating = window_pane_is_floating(wp);
|
||||||
|
|
||||||
flags = is_floating ? SPAWN_FLOATING : 0;
|
flags = is_floating ? SPAWN_FLOATING : 0;
|
||||||
if (args_has(args, 'b'))
|
if (args_has(args, 'b'))
|
||||||
|
|||||||
@@ -79,12 +79,6 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (src_wp == dst_wp)
|
if (src_wp == dst_wp)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (window_pane_is_floating(src_wp) ||
|
|
||||||
window_pane_is_floating(dst_wp)) {
|
|
||||||
cmdq_error(item, "cannot swap floating panes");
|
|
||||||
return (CMD_RETURN_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
server_client_remove_pane(src_wp);
|
server_client_remove_pane(src_wp);
|
||||||
server_client_remove_pane(dst_wp);
|
server_client_remove_pane(dst_wp);
|
||||||
|
|
||||||
|
|||||||
6
cmd.c
6
cmd.c
@@ -69,6 +69,7 @@ extern const struct cmd_entry cmd_load_buffer_entry;
|
|||||||
extern const struct cmd_entry cmd_lock_client_entry;
|
extern const struct cmd_entry cmd_lock_client_entry;
|
||||||
extern const struct cmd_entry cmd_lock_server_entry;
|
extern const struct cmd_entry cmd_lock_server_entry;
|
||||||
extern const struct cmd_entry cmd_lock_session_entry;
|
extern const struct cmd_entry cmd_lock_session_entry;
|
||||||
|
extern const struct cmd_entry cmd_hide_pane_entry;
|
||||||
extern const struct cmd_entry cmd_move_pane_entry;
|
extern const struct cmd_entry cmd_move_pane_entry;
|
||||||
extern const struct cmd_entry cmd_move_window_entry;
|
extern const struct cmd_entry cmd_move_window_entry;
|
||||||
extern const struct cmd_entry cmd_new_pane_entry;
|
extern const struct cmd_entry cmd_new_pane_entry;
|
||||||
@@ -106,6 +107,7 @@ extern const struct cmd_entry cmd_show_environment_entry;
|
|||||||
extern const struct cmd_entry cmd_show_hooks_entry;
|
extern const struct cmd_entry cmd_show_hooks_entry;
|
||||||
extern const struct cmd_entry cmd_show_messages_entry;
|
extern const struct cmd_entry cmd_show_messages_entry;
|
||||||
extern const struct cmd_entry cmd_show_options_entry;
|
extern const struct cmd_entry cmd_show_options_entry;
|
||||||
|
extern const struct cmd_entry cmd_show_pane_entry;
|
||||||
extern const struct cmd_entry cmd_show_prompt_history_entry;
|
extern const struct cmd_entry cmd_show_prompt_history_entry;
|
||||||
extern const struct cmd_entry cmd_show_window_options_entry;
|
extern const struct cmd_entry cmd_show_window_options_entry;
|
||||||
extern const struct cmd_entry cmd_source_file_entry;
|
extern const struct cmd_entry cmd_source_file_entry;
|
||||||
@@ -115,6 +117,7 @@ extern const struct cmd_entry cmd_suspend_client_entry;
|
|||||||
extern const struct cmd_entry cmd_swap_pane_entry;
|
extern const struct cmd_entry cmd_swap_pane_entry;
|
||||||
extern const struct cmd_entry cmd_swap_window_entry;
|
extern const struct cmd_entry cmd_swap_window_entry;
|
||||||
extern const struct cmd_entry cmd_switch_client_entry;
|
extern const struct cmd_entry cmd_switch_client_entry;
|
||||||
|
extern const struct cmd_entry cmd_switch_mode_entry;
|
||||||
extern const struct cmd_entry cmd_unbind_key_entry;
|
extern const struct cmd_entry cmd_unbind_key_entry;
|
||||||
extern const struct cmd_entry cmd_unlink_window_entry;
|
extern const struct cmd_entry cmd_unlink_window_entry;
|
||||||
extern const struct cmd_entry cmd_wait_for_entry;
|
extern const struct cmd_entry cmd_wait_for_entry;
|
||||||
@@ -162,6 +165,7 @@ const struct cmd_entry *cmd_table[] = {
|
|||||||
&cmd_lock_client_entry,
|
&cmd_lock_client_entry,
|
||||||
&cmd_lock_server_entry,
|
&cmd_lock_server_entry,
|
||||||
&cmd_lock_session_entry,
|
&cmd_lock_session_entry,
|
||||||
|
&cmd_hide_pane_entry,
|
||||||
&cmd_move_pane_entry,
|
&cmd_move_pane_entry,
|
||||||
&cmd_move_window_entry,
|
&cmd_move_window_entry,
|
||||||
&cmd_new_pane_entry,
|
&cmd_new_pane_entry,
|
||||||
@@ -199,6 +203,7 @@ const struct cmd_entry *cmd_table[] = {
|
|||||||
&cmd_show_hooks_entry,
|
&cmd_show_hooks_entry,
|
||||||
&cmd_show_messages_entry,
|
&cmd_show_messages_entry,
|
||||||
&cmd_show_options_entry,
|
&cmd_show_options_entry,
|
||||||
|
&cmd_show_pane_entry,
|
||||||
&cmd_show_prompt_history_entry,
|
&cmd_show_prompt_history_entry,
|
||||||
&cmd_show_window_options_entry,
|
&cmd_show_window_options_entry,
|
||||||
&cmd_source_file_entry,
|
&cmd_source_file_entry,
|
||||||
@@ -208,6 +213,7 @@ const struct cmd_entry *cmd_table[] = {
|
|||||||
&cmd_swap_pane_entry,
|
&cmd_swap_pane_entry,
|
||||||
&cmd_swap_window_entry,
|
&cmd_swap_window_entry,
|
||||||
&cmd_switch_client_entry,
|
&cmd_switch_client_entry,
|
||||||
|
&cmd_switch_mode_entry,
|
||||||
&cmd_unbind_key_entry,
|
&cmd_unbind_key_entry,
|
||||||
&cmd_unlink_window_entry,
|
&cmd_unlink_window_entry,
|
||||||
&cmd_wait_for_entry,
|
&cmd_wait_for_entry,
|
||||||
|
|||||||
146
colour.c
146
colour.c
@@ -26,6 +26,85 @@
|
|||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
|
||||||
|
/* Theme colour slots and their server options. */
|
||||||
|
static const struct {
|
||||||
|
const char *name;
|
||||||
|
const char *dark_option;
|
||||||
|
const char *light_option;
|
||||||
|
int terminal_colour;
|
||||||
|
} colour_theme_table[] = {
|
||||||
|
{ "themeblack",
|
||||||
|
"dark-theme-black",
|
||||||
|
"light-theme-black",
|
||||||
|
0
|
||||||
|
},
|
||||||
|
{ "themewhite",
|
||||||
|
"dark-theme-white",
|
||||||
|
"light-theme-white",
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{ "themelightgrey",
|
||||||
|
"dark-theme-light-grey",
|
||||||
|
"light-theme-light-grey",
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{ "themedarkgrey",
|
||||||
|
"dark-theme-dark-grey",
|
||||||
|
"light-theme-dark-grey",
|
||||||
|
0
|
||||||
|
},
|
||||||
|
{ "themegreen",
|
||||||
|
"dark-theme-green",
|
||||||
|
"light-theme-green",
|
||||||
|
2
|
||||||
|
},
|
||||||
|
{ "themeyellow",
|
||||||
|
"dark-theme-yellow",
|
||||||
|
"light-theme-yellow",
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{ "themered",
|
||||||
|
"dark-theme-red",
|
||||||
|
"light-theme-red",
|
||||||
|
1
|
||||||
|
},
|
||||||
|
{ "themeblue",
|
||||||
|
"dark-theme-blue",
|
||||||
|
"light-theme-blue",
|
||||||
|
4
|
||||||
|
},
|
||||||
|
{ "themecyan",
|
||||||
|
"dark-theme-cyan",
|
||||||
|
"light-theme-cyan",
|
||||||
|
6
|
||||||
|
},
|
||||||
|
{ "thememagenta",
|
||||||
|
"dark-theme-magenta",
|
||||||
|
"light-theme-magenta",
|
||||||
|
5
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get theme colour option. */
|
||||||
|
const char *
|
||||||
|
colour_theme_option(u_int n, enum client_theme theme)
|
||||||
|
{
|
||||||
|
if (n >= nitems(colour_theme_table))
|
||||||
|
return (NULL);
|
||||||
|
if (theme == THEME_LIGHT)
|
||||||
|
return (colour_theme_table[n].light_option);
|
||||||
|
return (colour_theme_table[n].dark_option);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get theme terminal colour. */
|
||||||
|
int
|
||||||
|
colour_theme_terminal_colour(u_int n)
|
||||||
|
{
|
||||||
|
if (n >= nitems(colour_theme_table))
|
||||||
|
return (8);
|
||||||
|
return (colour_theme_table[n].terminal_colour);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
colour_dist_sq(int R, int G, int B, int r, int g, int b)
|
colour_dist_sq(int R, int G, int B, int r, int g, int b)
|
||||||
{
|
{
|
||||||
@@ -126,7 +205,7 @@ colour_dim(int c, u_int dim)
|
|||||||
{
|
{
|
||||||
u_char r, g, b;
|
u_char r, g, b;
|
||||||
|
|
||||||
if (dim == 0 || COLOUR_DEFAULT(c))
|
if (dim == 0 || COLOUR_DEFAULT(c) || (c & COLOUR_FLAG_THEME))
|
||||||
return (c);
|
return (c);
|
||||||
if (dim >= 100)
|
if (dim >= 100)
|
||||||
return (colour_join_rgb(0, 0, 0));
|
return (colour_join_rgb(0, 0, 0));
|
||||||
@@ -152,6 +231,13 @@ colour_tostring(int c)
|
|||||||
if (c == -1)
|
if (c == -1)
|
||||||
return ("none");
|
return ("none");
|
||||||
|
|
||||||
|
if (c & COLOUR_FLAG_THEME) {
|
||||||
|
c &= 0xff;
|
||||||
|
if (c >= 0 && (u_int)c < nitems(colour_theme_table))
|
||||||
|
return (colour_theme_table[c].name);
|
||||||
|
return ("invalid");
|
||||||
|
}
|
||||||
|
|
||||||
if (c & COLOUR_FLAG_RGB) {
|
if (c & COLOUR_FLAG_RGB) {
|
||||||
colour_split_rgb(c, &r, &g, &b);
|
colour_split_rgb(c, &r, &g, &b);
|
||||||
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
|
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
|
||||||
@@ -204,6 +290,58 @@ colour_tostring(int c)
|
|||||||
return ("invalid");
|
return ("invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert colour to an SGR escape sequence. */
|
||||||
|
const char *
|
||||||
|
colour_toescape(struct client *c, int colour, int bg)
|
||||||
|
{
|
||||||
|
static char s[32];
|
||||||
|
u_char r, g, b;
|
||||||
|
int n, flags = (TERM_256COLOURS|TERM_RGBCOLOURS);
|
||||||
|
u_int o = (bg ? 40 : 30);
|
||||||
|
|
||||||
|
if (c != NULL && (c->tty.flags & TTY_OPENED) && c->tty.term != NULL)
|
||||||
|
flags = c->tty.term->flags;
|
||||||
|
|
||||||
|
if (colour & COLOUR_FLAG_THEME) {
|
||||||
|
n = colour & 0xff;
|
||||||
|
if (c != NULL && (u_int)n < COLOUR_THEME_COUNT)
|
||||||
|
colour = c->theme_colours[n];
|
||||||
|
else
|
||||||
|
colour = colour_theme_terminal_colour(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colour == 8 || colour == 9) {
|
||||||
|
xsnprintf(s, sizeof s, "\033[%dm", o + 9);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((~flags & TERM_RGBCOLOURS) & (colour & COLOUR_FLAG_RGB)) {
|
||||||
|
colour_split_rgb(colour, &r, &g, &b);
|
||||||
|
colour = colour_find_rgb(r, g, b);
|
||||||
|
}
|
||||||
|
if ((~flags & TERM_256COLOURS) & (colour & COLOUR_FLAG_256))
|
||||||
|
colour = colour_256to16(colour);
|
||||||
|
|
||||||
|
if (colour & COLOUR_FLAG_RGB) {
|
||||||
|
colour_split_rgb(colour, &r, &g, &b);
|
||||||
|
xsnprintf(s, sizeof s, "\033[%d;2;%u;%u;%um", o + 8, r, g, b);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
if (colour & COLOUR_FLAG_256) {
|
||||||
|
xsnprintf(s, sizeof s, "\033[%d;5;%um", o + 8, colour & 0xff);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
if (colour >= 0 && colour <= 7) {
|
||||||
|
xsnprintf(s, sizeof s, "\033[%dm", colour + o);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
if (colour >= 90 && colour <= 97) {
|
||||||
|
xsnprintf(s, sizeof s, "\033[%dm", colour + o - 30);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert background colour to theme. */
|
/* Convert background colour to theme. */
|
||||||
enum client_theme
|
enum client_theme
|
||||||
colour_totheme(int c)
|
colour_totheme(int c)
|
||||||
@@ -252,6 +390,7 @@ colour_fromstring(const char *s)
|
|||||||
const char *cp;
|
const char *cp;
|
||||||
int n;
|
int n;
|
||||||
u_char r, g, b;
|
u_char r, g, b;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
if (*s == '#' && strlen(s) == 7) {
|
if (*s == '#' && strlen(s) == 7) {
|
||||||
for (cp = s + 1; isxdigit((u_char) *cp); cp++)
|
for (cp = s + 1; isxdigit((u_char) *cp); cp++)
|
||||||
@@ -282,6 +421,11 @@ colour_fromstring(const char *s)
|
|||||||
if (strcasecmp(s, "terminal") == 0)
|
if (strcasecmp(s, "terminal") == 0)
|
||||||
return (9);
|
return (9);
|
||||||
|
|
||||||
|
for (i = 0; i < nitems(colour_theme_table); i++) {
|
||||||
|
if (strcasecmp(s, colour_theme_table[i].name) == 0)
|
||||||
|
return (i|COLOUR_FLAG_THEME);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
|
if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
|
||||||
return (0);
|
return (0);
|
||||||
if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
|
if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
|
||||||
|
|||||||
@@ -710,6 +710,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
|||||||
"AFTER" };
|
"AFTER" };
|
||||||
size_t size = strlen(expanded);
|
size_t size = strlen(expanded);
|
||||||
struct screen *os = octx->s, s[TOTAL];
|
struct screen *os = octx->s, s[TOTAL];
|
||||||
|
struct hyperlinks *hl = os->hyperlinks;
|
||||||
struct screen_write_ctx ctx[TOTAL];
|
struct screen_write_ctx ctx[TOTAL];
|
||||||
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
|
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
|
||||||
u_int map[] = { LEFT,
|
u_int map[] = { LEFT,
|
||||||
@@ -723,7 +724,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
|||||||
struct grid_cell gc, current_default, base_default;
|
struct grid_cell gc, current_default, base_default;
|
||||||
struct style sy, saved_sy;
|
struct style sy, saved_sy;
|
||||||
struct utf8_data *ud = &sy.gc.data;
|
struct utf8_data *ud = &sy.gc.data;
|
||||||
const char *cp, *end;
|
const char *cp, *end, *link_uri;
|
||||||
enum utf8_state more;
|
enum utf8_state more;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
struct format_range *fr = NULL, *fr1;
|
struct format_range *fr = NULL, *fr1;
|
||||||
@@ -838,6 +839,17 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
|||||||
sy.gc.fg = base->fg;
|
sy.gc.fg = base->fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resolve any hyperlink and store it in the cell. The URI
|
||||||
|
* doubles as the internal ID so repeated links share one entry
|
||||||
|
* and the ID stays stable across redraws.
|
||||||
|
*/
|
||||||
|
link_uri = style_link(&sy);
|
||||||
|
if (link_uri != NULL && hl != NULL)
|
||||||
|
sy.gc.link = hyperlinks_put(hl, link_uri, link_uri);
|
||||||
|
else
|
||||||
|
sy.gc.link = 0;
|
||||||
|
|
||||||
/* If this style has a fill colour, store it for later. */
|
/* If this style has a fill colour, store it for later. */
|
||||||
if (sy.fill != 8)
|
if (sy.fill != 8)
|
||||||
fill = sy.fill;
|
fill = sy.fill;
|
||||||
|
|||||||
101
format.c
101
format.c
@@ -121,6 +121,8 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
|||||||
#define FORMAT_CLIENT_TERMCAP 0x1000000
|
#define FORMAT_CLIENT_TERMCAP 0x1000000
|
||||||
#define FORMAT_CLIENT_TERMFEAT 0x2000000
|
#define FORMAT_CLIENT_TERMFEAT 0x2000000
|
||||||
#define FORMAT_CLIENT_ENVIRON 0x4000000
|
#define FORMAT_CLIENT_ENVIRON 0x4000000
|
||||||
|
#define FORMAT_COLOUR_ESC_FG 0x8000000
|
||||||
|
#define FORMAT_COLOUR_ESC_BG 0x10000000
|
||||||
|
|
||||||
/* Limit on recursion. */
|
/* Limit on recursion. */
|
||||||
#define FORMAT_LOOP_LIMIT 100
|
#define FORMAT_LOOP_LIMIT 100
|
||||||
@@ -2167,6 +2169,20 @@ format_cb_pane_height(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_hidden_flag. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_hidden_flag(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ft->wp;
|
||||||
|
|
||||||
|
if (wp != NULL) {
|
||||||
|
if (window_pane_is_hidden(wp))
|
||||||
|
return (xstrdup("1"));
|
||||||
|
return (xstrdup("0"));
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for pane_id. */
|
/* Callback for pane_id. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_pane_id(struct format_tree *ft)
|
format_cb_pane_id(struct format_tree *ft)
|
||||||
@@ -3472,6 +3488,9 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "pane_height", FORMAT_TABLE_STRING,
|
{ "pane_height", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_height
|
format_cb_pane_height
|
||||||
},
|
},
|
||||||
|
{ "pane_hidden_flag", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_hidden_flag
|
||||||
|
},
|
||||||
{ "pane_id", FORMAT_TABLE_STRING,
|
{ "pane_id", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_id
|
format_cb_pane_id
|
||||||
},
|
},
|
||||||
@@ -4472,7 +4491,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* Check single character modifiers with no arguments. */
|
/* Check single character modifiers with no arguments. */
|
||||||
if (strchr("labcdnwETSWPL!<>", cp[0]) != NULL &&
|
if (strchr("labdnwETSWPL!<>", cp[0]) != NULL &&
|
||||||
format_is_end(cp[1])) {
|
format_is_end(cp[1])) {
|
||||||
format_add_modifier(&list, count, cp, 1, NULL, 0);
|
format_add_modifier(&list, count, cp, 1, NULL, 0);
|
||||||
cp++;
|
cp++;
|
||||||
@@ -4494,7 +4513,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now try single character with arguments. */
|
/* Now try single character with arguments. */
|
||||||
if (strchr("ImCLNPSst=pReqW", cp[0]) == NULL)
|
if (strchr("ImCLNPSst=pReqWc", cp[0]) == NULL)
|
||||||
break;
|
break;
|
||||||
c = cp[0];
|
c = cp[0];
|
||||||
|
|
||||||
@@ -4555,6 +4574,47 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
|||||||
return (list);
|
return (list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Match using the fuzzy matcher. */
|
||||||
|
static char *
|
||||||
|
format_match_fuzzy(const char *pattern, const char *text, int positions)
|
||||||
|
{
|
||||||
|
struct evbuffer *buffer;
|
||||||
|
bitstr_t *bs;
|
||||||
|
char *value;
|
||||||
|
size_t size;
|
||||||
|
u_int i, width;
|
||||||
|
|
||||||
|
width = format_width(text);
|
||||||
|
if (width == 0)
|
||||||
|
width = 1;
|
||||||
|
bs = fuzzy_match(pattern, text, width, NULL);
|
||||||
|
if (bs == NULL)
|
||||||
|
return (xstrdup(positions ? "" : "0"));
|
||||||
|
|
||||||
|
if (!positions) {
|
||||||
|
free(bs);
|
||||||
|
return (xstrdup("1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = evbuffer_new();
|
||||||
|
if (buffer == NULL)
|
||||||
|
fatalx("out of memory");
|
||||||
|
for (i = 0; i < width; i++) {
|
||||||
|
if (!bit_test(bs, i))
|
||||||
|
continue;
|
||||||
|
if (EVBUFFER_LENGTH(buffer) != 0)
|
||||||
|
evbuffer_add(buffer, ",", 1);
|
||||||
|
evbuffer_add_printf(buffer, "%u", i);
|
||||||
|
}
|
||||||
|
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
|
||||||
|
value = xmemdup(EVBUFFER_DATA(buffer), size);
|
||||||
|
else
|
||||||
|
value = xstrdup("");
|
||||||
|
evbuffer_free(buffer);
|
||||||
|
free(bs);
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
/* Match against an fnmatch(3) pattern or regular expression. */
|
/* Match against an fnmatch(3) pattern or regular expression. */
|
||||||
static char *
|
static char *
|
||||||
format_match(struct format_modifier *fm, const char *pattern, const char *text)
|
format_match(struct format_modifier *fm, const char *pattern, const char *text)
|
||||||
@@ -4565,6 +4625,10 @@ format_match(struct format_modifier *fm, const char *pattern, const char *text)
|
|||||||
|
|
||||||
if (fm->argc >= 1)
|
if (fm->argc >= 1)
|
||||||
s = fm->argv[0];
|
s = fm->argv[0];
|
||||||
|
if (strchr(s, 'p') != NULL)
|
||||||
|
return (format_match_fuzzy(pattern, text, 1));
|
||||||
|
if (strchr(s, 'z') != NULL)
|
||||||
|
return (format_match_fuzzy(pattern, text, 0));
|
||||||
if (strchr(s, 'r') == NULL) {
|
if (strchr(s, 'r') == NULL) {
|
||||||
if (strchr(s, 'i') != NULL)
|
if (strchr(s, 'i') != NULL)
|
||||||
flags |= FNM_CASEFOLD;
|
flags |= FNM_CASEFOLD;
|
||||||
@@ -5227,6 +5291,12 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
modifiers |= FORMAT_COLOUR;
|
modifiers |= FORMAT_COLOUR;
|
||||||
|
if (fm->argc < 1)
|
||||||
|
break;
|
||||||
|
if (strchr(fm->argv[0], 'f') != NULL)
|
||||||
|
modifiers |= FORMAT_COLOUR_ESC_FG;
|
||||||
|
if (strchr(fm->argv[0], 'b') != NULL)
|
||||||
|
modifiers |= FORMAT_COLOUR_ESC_BG;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
modifiers |= FORMAT_DIRNAME;
|
modifiers |= FORMAT_DIRNAME;
|
||||||
@@ -5425,11 +5495,28 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
/* Is this a colour? */
|
/* Is this a colour? */
|
||||||
if (modifiers & FORMAT_COLOUR) {
|
if (modifiers & FORMAT_COLOUR) {
|
||||||
new = format_expand1(es, copy);
|
new = format_expand1(es, copy);
|
||||||
c = colour_fromstring(new);
|
if (modifiers & (FORMAT_COLOUR_ESC_FG|FORMAT_COLOUR_ESC_BG)) {
|
||||||
if (c == -1 || (c = colour_force_rgb(c)) == -1)
|
if (strcasecmp(new, "none") == 0)
|
||||||
value = xstrdup("");
|
value = xstrdup("\033[0m");
|
||||||
else
|
else if ((c = colour_fromstring(new)) == -1)
|
||||||
xasprintf(&value, "%06x", c & 0xffffff);
|
value = xstrdup("");
|
||||||
|
else {
|
||||||
|
if (modifiers & FORMAT_COLOUR_ESC_BG)
|
||||||
|
cp = colour_toescape(ft->c, c, 1);
|
||||||
|
else
|
||||||
|
cp = colour_toescape(ft->c, c, 0);
|
||||||
|
if (cp == NULL)
|
||||||
|
value = xstrdup("");
|
||||||
|
else
|
||||||
|
value = xstrdup(cp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c = colour_fromstring(new);
|
||||||
|
if (c == -1 || (c = colour_force_rgb(c)) == -1)
|
||||||
|
value = xstrdup("");
|
||||||
|
else
|
||||||
|
xasprintf(&value, "%06x", c & 0xffffff);
|
||||||
|
}
|
||||||
free(new);
|
free(new);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|||||||
655
fuzzy.c
Normal file
655
fuzzy.c
Normal file
@@ -0,0 +1,655 @@
|
|||||||
|
/* $OpenBSD$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fuzzy matching in the style of fzf. The pattern is split into groups by |
|
||||||
|
* and each group is split on spaces into terms. A row matches if any group
|
||||||
|
* matches; within a group all positive terms must match and all inverse terms
|
||||||
|
* must not match.
|
||||||
|
*
|
||||||
|
* Plain positive terms are fuzzy subsequences. A leading ' makes a term an
|
||||||
|
* exact substring match, ^ anchors a term at the start and $ anchors it at
|
||||||
|
* the end. A leading ! inverts the term. Plain inverse terms are exact
|
||||||
|
* substring matches rather than inverse fuzzy matches, like fzf.
|
||||||
|
*
|
||||||
|
* Both the pattern and the text are UTF-8. The text may contain tmux style
|
||||||
|
* directives (#[...]); these and their contents are invisible to matching and
|
||||||
|
* occupy no columns, but align= styles do move the surrounding text and are
|
||||||
|
* accounted for exactly as format_draw lays it out (the no-list layout, see
|
||||||
|
* format_draw_none). Matching is smart-case: case is ignored unless the pattern
|
||||||
|
* contains an uppercase character (ASCII case folding only; other characters
|
||||||
|
* are compared exactly by their UTF-8 data).
|
||||||
|
*
|
||||||
|
* On a match a bitstr_t of the requested display width is returned with a bit
|
||||||
|
* set for every column occupied by a matched character, so the caller can
|
||||||
|
* highlight them; NULL is returned if there is no match. A cheap fzf-style
|
||||||
|
* score (matches at the start, after word boundaries and in contiguous runs
|
||||||
|
* score higher) is also produced so callers can rank best-match-first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FUZZY_BONUS_EXACT 1000
|
||||||
|
#define FUZZY_BONUS_PREFIX 200
|
||||||
|
#define FUZZY_BONUS_SUFFIX 100
|
||||||
|
#define FUZZY_BONUS_START 12
|
||||||
|
#define FUZZY_BONUS_BOUNDARY 8
|
||||||
|
#define FUZZY_BONUS_CONSECUTIVE 6
|
||||||
|
#define FUZZY_PENALTY_LEADING 1
|
||||||
|
#define FUZZY_PENALTY_LEADING_MAX 10
|
||||||
|
#define FUZZY_PENALTY_GAP 1
|
||||||
|
|
||||||
|
/* A single visible character of the text. */
|
||||||
|
struct fuzzy_char {
|
||||||
|
enum style_align align;
|
||||||
|
struct utf8_data ud; /* original UTF-8 data */
|
||||||
|
u_int width; /* display width */
|
||||||
|
u_int offset; /* within its alignment */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* One parsed query term. */
|
||||||
|
struct fuzzy_term {
|
||||||
|
int inverse;
|
||||||
|
int exact;
|
||||||
|
int prefix;
|
||||||
|
int suffix;
|
||||||
|
const char *text;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Is this character a word boundary, so a match after it scores higher? */
|
||||||
|
static int
|
||||||
|
fuzzy_is_boundary(const struct utf8_data *ud)
|
||||||
|
{
|
||||||
|
static const char *boundary = " -_/.:";
|
||||||
|
|
||||||
|
if (ud->size != 1)
|
||||||
|
return (0);
|
||||||
|
return (strchr(boundary, ud->data[0]) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two characters, folding ASCII case if wanted. UTF-8 is compared
|
||||||
|
* directly without case folding.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fuzzy_char_equal(const struct utf8_data *a, const struct utf8_data *b, int fold)
|
||||||
|
{
|
||||||
|
if (fold &&
|
||||||
|
a->size == 1 &&
|
||||||
|
b->size == 1 &&
|
||||||
|
a->data[0] < 0x80 &&
|
||||||
|
b->data[0] < 0x80)
|
||||||
|
return (tolower(a->data[0]) == tolower(b->data[0]));
|
||||||
|
return (a->size == b->size && memcmp(a->data, b->data, a->size) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Map a style alignment onto one of the four layout columns. */
|
||||||
|
static enum style_align
|
||||||
|
fuzzy_align(enum style_align align)
|
||||||
|
{
|
||||||
|
if (align == STYLE_ALIGN_DEFAULT)
|
||||||
|
return (STYLE_ALIGN_LEFT);
|
||||||
|
return (align);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a visible character to the array, updating the alignment width. */
|
||||||
|
static void
|
||||||
|
fuzzy_add(struct fuzzy_char **cs, u_int *ncs, u_int *alloc, enum style_align a,
|
||||||
|
const struct utf8_data *ud, u_int *widths)
|
||||||
|
{
|
||||||
|
struct fuzzy_char *fc;
|
||||||
|
|
||||||
|
if (*ncs == *alloc) {
|
||||||
|
*alloc = (*alloc == 0) ? 64 : *alloc * 2;
|
||||||
|
*cs = xreallocarray(*cs, *alloc, sizeof **cs);
|
||||||
|
}
|
||||||
|
fc = &(*cs)[(*ncs)++];
|
||||||
|
fc->align = a;
|
||||||
|
memcpy(&fc->ud, ud, sizeof fc->ud);
|
||||||
|
fc->width = ud->width;
|
||||||
|
fc->offset = widths[a];
|
||||||
|
widths[a] += ud->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode a character as UTF-8. */
|
||||||
|
static const char *
|
||||||
|
fuzzy_decode_one(const char *cp, const char *end, struct utf8_data *ud)
|
||||||
|
{
|
||||||
|
enum utf8_state more;
|
||||||
|
const char *start = cp;
|
||||||
|
|
||||||
|
if ((more = utf8_open(ud, (u_char)*cp)) == UTF8_MORE) {
|
||||||
|
while (++cp != end && more == UTF8_MORE)
|
||||||
|
more = utf8_append(ud, (u_char)*cp);
|
||||||
|
if (more == UTF8_DONE)
|
||||||
|
return (cp);
|
||||||
|
cp = start;
|
||||||
|
}
|
||||||
|
utf8_set(ud, (u_char)*cp);
|
||||||
|
return (cp + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan the text into an array of visible characters, skipping styles and
|
||||||
|
* recording the alignment and intra-alignment offset of each. Returns the
|
||||||
|
* array and its length and fills in the per-alignment widths.
|
||||||
|
*/
|
||||||
|
static struct fuzzy_char *
|
||||||
|
fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
|
||||||
|
{
|
||||||
|
struct fuzzy_char *cs = NULL;
|
||||||
|
u_int alloc = 0, n, leading, i;
|
||||||
|
enum style_align current = STYLE_ALIGN_LEFT;
|
||||||
|
struct style sy;
|
||||||
|
const char *cp = text, *textend = text + strlen(text);
|
||||||
|
const char *end;
|
||||||
|
struct utf8_data ud, hash, bracket;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
*ncs = 0;
|
||||||
|
memset(widths, 0, sizeof *widths * (STYLE_ALIGN_ABSOLUTE_CENTRE + 1));
|
||||||
|
style_set(&sy, &grid_default_cell);
|
||||||
|
utf8_set(&hash, '#');
|
||||||
|
utf8_set(&bracket, '[');
|
||||||
|
|
||||||
|
while (*cp != '\0') {
|
||||||
|
/* Handle a run of #s, which may introduce a style. */
|
||||||
|
if (*cp == '#') {
|
||||||
|
for (n = 0; cp[n] == '#'; n++)
|
||||||
|
/* nothing */;
|
||||||
|
if (cp[n] != '[') {
|
||||||
|
/* Escaped #s: ##->#, so half (rounded up). */
|
||||||
|
leading = (n % 2 == 0) ? n / 2 : n / 2 + 1;
|
||||||
|
for (i = 0; i < leading; i++) {
|
||||||
|
fuzzy_add(&cs, ncs, &alloc, current,
|
||||||
|
&hash, widths);
|
||||||
|
}
|
||||||
|
cp += n;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Even count: all #s escaped, the [ is literal. */
|
||||||
|
for (i = 0; i < n / 2; i++)
|
||||||
|
fuzzy_add(&cs, ncs, &alloc, current, &hash,
|
||||||
|
widths);
|
||||||
|
if (n % 2 == 0) {
|
||||||
|
fuzzy_add(&cs, ncs, &alloc, current, &bracket,
|
||||||
|
widths);
|
||||||
|
cp += n + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Odd count: this is a style, find and parse it. */
|
||||||
|
end = format_skip(cp + n + 1, "]");
|
||||||
|
if (end == NULL)
|
||||||
|
break;
|
||||||
|
tmp = xstrndup(cp + n + 1, end - (cp + n + 1));
|
||||||
|
if (style_parse(&sy, &grid_default_cell, tmp) == 0)
|
||||||
|
current = fuzzy_align(sy.align);
|
||||||
|
free(tmp);
|
||||||
|
cp = end + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode one character, multibyte or single byte. */
|
||||||
|
cp = fuzzy_decode_one(cp, textend, &ud);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip non-printable single bytes (control characters and raw
|
||||||
|
* bytes left over from a failed decode); keep printable ASCII
|
||||||
|
* and any decoded UTF-8.
|
||||||
|
*/
|
||||||
|
if (ud.size == 1 && (ud.data[0] <= 0x1f || ud.data[0] >= 0x7f))
|
||||||
|
continue;
|
||||||
|
fuzzy_add(&cs, ncs, &alloc, current, &ud, widths);
|
||||||
|
}
|
||||||
|
return (cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Work out the display column of a visible character given the trimmed widths
|
||||||
|
* and start columns of each alignment. Returns 0 and sets the column if the
|
||||||
|
* character is visible, otherwise returns -1.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src,
|
||||||
|
const u_int *vis, u_int *column)
|
||||||
|
{
|
||||||
|
enum style_align a = fc->align;
|
||||||
|
|
||||||
|
if (fc->offset < src[a] || fc->offset >= src[a] + vis[a])
|
||||||
|
return (-1);
|
||||||
|
*column = start[a] + (fc->offset - src[a]);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode a UTF-8 term into an array of characters. */
|
||||||
|
static u_int
|
||||||
|
fuzzy_decode(const char *tok, size_t len, struct utf8_data *out)
|
||||||
|
{
|
||||||
|
const char *cp = tok, *end = tok + len;
|
||||||
|
u_int n = 0;
|
||||||
|
|
||||||
|
while (cp != end)
|
||||||
|
cp = fuzzy_decode_one(cp, end, &out[n++]);
|
||||||
|
return (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the score for a fuzzy token matched at the given positions. */
|
||||||
|
static int
|
||||||
|
fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
|
||||||
|
{
|
||||||
|
u_int i, gap, span;
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
if (npos == 0)
|
||||||
|
return (0);
|
||||||
|
if (pos[0] == 0)
|
||||||
|
score += FUZZY_BONUS_START;
|
||||||
|
else {
|
||||||
|
if (fuzzy_is_boundary(&cs[pos[0] - 1].ud))
|
||||||
|
score += FUZZY_BONUS_BOUNDARY;
|
||||||
|
if (pos[0] < FUZZY_PENALTY_LEADING_MAX)
|
||||||
|
score -= pos[0] * FUZZY_PENALTY_LEADING;
|
||||||
|
else {
|
||||||
|
score -= FUZZY_PENALTY_LEADING_MAX *
|
||||||
|
FUZZY_PENALTY_LEADING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 1; i < npos; i++) {
|
||||||
|
if (pos[i] == pos[i - 1] + 1)
|
||||||
|
score += FUZZY_BONUS_CONSECUTIVE;
|
||||||
|
else if (fuzzy_is_boundary(&cs[pos[i] - 1].ud))
|
||||||
|
score += FUZZY_BONUS_BOUNDARY;
|
||||||
|
}
|
||||||
|
span = pos[npos - 1] - pos[0] + 1;
|
||||||
|
gap = span - npos;
|
||||||
|
score -= gap * FUZZY_PENALTY_GAP;
|
||||||
|
return (score);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Match a token as a subsequence of the visible characters. Returns if the
|
||||||
|
* token matches.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
|
||||||
|
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
|
||||||
|
{
|
||||||
|
u_int pi, ci, *pos;
|
||||||
|
int found, value;
|
||||||
|
|
||||||
|
if (toklen == 0 || ncs == 0)
|
||||||
|
return (0);
|
||||||
|
pos = xcalloc(toklen, sizeof *pos);
|
||||||
|
|
||||||
|
/* First find a subsequence from the start. */
|
||||||
|
ci = 0;
|
||||||
|
for (pi = 0; pi < toklen; pi++) {
|
||||||
|
while (ci != ncs &&
|
||||||
|
!fuzzy_char_equal(&tok[pi], &cs[ci].ud, fold))
|
||||||
|
ci++;
|
||||||
|
if (ci == ncs) {
|
||||||
|
free(pos);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
pos[pi] = ci++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then compact it backwards to prefer a shorter span. */
|
||||||
|
ci = pos[toklen - 1];
|
||||||
|
for (pi = toklen; pi > 0; pi--) {
|
||||||
|
found = 0;
|
||||||
|
for (;;) {
|
||||||
|
if (fuzzy_char_equal(&tok[pi - 1], &cs[ci].ud, fold)) {
|
||||||
|
pos[pi - 1] = ci;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ci == 0)
|
||||||
|
break;
|
||||||
|
ci--;
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
free(pos);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (pi != 1)
|
||||||
|
ci--;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = fuzzy_score_positions(pos, toklen, cs);
|
||||||
|
*score += value;
|
||||||
|
for (pi = 0; pi < toklen; pi++)
|
||||||
|
matched[pos[pi]] = 1;
|
||||||
|
free(pos);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Score an exact, prefix or suffix match. */
|
||||||
|
static int
|
||||||
|
fuzzy_score_exact(u_int start, u_int toklen, u_int ncs,
|
||||||
|
const struct fuzzy_char *cs, int prefix, int suffix)
|
||||||
|
{
|
||||||
|
int score;
|
||||||
|
|
||||||
|
score = FUZZY_BONUS_EXACT + toklen * FUZZY_BONUS_CONSECUTIVE;
|
||||||
|
if (prefix)
|
||||||
|
score += FUZZY_BONUS_PREFIX;
|
||||||
|
if (suffix)
|
||||||
|
score += FUZZY_BONUS_SUFFIX;
|
||||||
|
if (start == 0)
|
||||||
|
score += FUZZY_BONUS_START;
|
||||||
|
else if (fuzzy_is_boundary(&cs[start - 1].ud))
|
||||||
|
score += FUZZY_BONUS_BOUNDARY;
|
||||||
|
if (start < FUZZY_PENALTY_LEADING_MAX)
|
||||||
|
score -= start * FUZZY_PENALTY_LEADING;
|
||||||
|
else
|
||||||
|
score -= FUZZY_PENALTY_LEADING_MAX * FUZZY_PENALTY_LEADING;
|
||||||
|
if (!prefix && !suffix)
|
||||||
|
score -= ncs - (start + toklen);
|
||||||
|
return (score);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match an exact, prefix or suffix term against the visible characters. */
|
||||||
|
static int
|
||||||
|
fuzzy_match_exact(const struct utf8_data *tok, u_int toklen,
|
||||||
|
struct fuzzy_char *cs, u_int ncs, int fold, int prefix, int suffix,
|
||||||
|
int *score, char *matched)
|
||||||
|
{
|
||||||
|
u_int start, end, i, j, best = 0;
|
||||||
|
int ok, found = 0, value, bestscore = 0;
|
||||||
|
|
||||||
|
if (toklen == 0 || toklen > ncs)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (prefix && suffix) {
|
||||||
|
if (toklen != ncs)
|
||||||
|
return (0);
|
||||||
|
start = 0;
|
||||||
|
end = 1;
|
||||||
|
} else if (prefix) {
|
||||||
|
start = 0;
|
||||||
|
end = 1;
|
||||||
|
} else if (suffix) {
|
||||||
|
start = ncs - toklen;
|
||||||
|
end = start + 1;
|
||||||
|
} else {
|
||||||
|
start = 0;
|
||||||
|
end = ncs - toklen + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = start; i < end; i++) {
|
||||||
|
ok = 1;
|
||||||
|
for (j = 0; j < toklen; j++) {
|
||||||
|
if (!fuzzy_char_equal(&tok[j], &cs[i + j].ud, fold)) {
|
||||||
|
ok = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
continue;
|
||||||
|
value = fuzzy_score_exact(i, toklen, ncs, cs, prefix, suffix);
|
||||||
|
if (!found || value > bestscore) {
|
||||||
|
found = 1;
|
||||||
|
best = i;
|
||||||
|
bestscore = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
return (0);
|
||||||
|
*score += bestscore;
|
||||||
|
if (matched != NULL) {
|
||||||
|
for (i = 0; i < toklen; i++)
|
||||||
|
matched[best + i] = 1;
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse one term. */
|
||||||
|
static int
|
||||||
|
fuzzy_parse_term(const char *start, const char *end, struct fuzzy_term *term)
|
||||||
|
{
|
||||||
|
memset(term, 0, sizeof *term);
|
||||||
|
if (start == end)
|
||||||
|
return (0);
|
||||||
|
if (*start == '!') {
|
||||||
|
term->inverse = 1;
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
if (start == end)
|
||||||
|
return (0);
|
||||||
|
if (*start == '\'') {
|
||||||
|
term->exact = 1;
|
||||||
|
start++;
|
||||||
|
} else if (*start == '^') {
|
||||||
|
term->exact = 1;
|
||||||
|
term->prefix = 1;
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
if (start == end)
|
||||||
|
return (0);
|
||||||
|
if (end[-1] == '$') {
|
||||||
|
term->exact = 1;
|
||||||
|
term->suffix = 1;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
if (start == end)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (term->inverse)
|
||||||
|
term->exact = 1;
|
||||||
|
term->text = start;
|
||||||
|
term->len = end - start;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match one parsed term. */
|
||||||
|
static int
|
||||||
|
fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok,
|
||||||
|
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
|
||||||
|
{
|
||||||
|
u_int toklen;
|
||||||
|
int value = 0, matched_term;
|
||||||
|
|
||||||
|
toklen = fuzzy_decode(term->text, term->len, tok);
|
||||||
|
if (term->exact) {
|
||||||
|
matched_term = fuzzy_match_exact(tok, toklen, cs, ncs, fold,
|
||||||
|
term->prefix, term->suffix, &value,
|
||||||
|
term->inverse ? NULL : matched);
|
||||||
|
} else {
|
||||||
|
matched_term = fuzzy_match_fuzzy(tok, toklen, cs, ncs, fold,
|
||||||
|
&value, term->inverse ? NULL : matched);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (term->inverse)
|
||||||
|
return (!matched_term);
|
||||||
|
if (!matched_term)
|
||||||
|
return (0);
|
||||||
|
*score += value;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match one AND group of terms. */
|
||||||
|
static int
|
||||||
|
fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
|
||||||
|
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
|
||||||
|
{
|
||||||
|
const char *cp = start, *sp;
|
||||||
|
struct fuzzy_term term;
|
||||||
|
int any = 0;
|
||||||
|
|
||||||
|
*score = 0;
|
||||||
|
while (cp != end) {
|
||||||
|
while (cp != end && *cp == ' ')
|
||||||
|
cp++;
|
||||||
|
if (cp == end)
|
||||||
|
break;
|
||||||
|
sp = cp;
|
||||||
|
while (cp != end && *cp != ' ')
|
||||||
|
cp++;
|
||||||
|
if (!fuzzy_parse_term(sp, cp, &term))
|
||||||
|
return (0);
|
||||||
|
any = 1;
|
||||||
|
if (!fuzzy_match_term(&term, tok, cs, ncs, fold, score,
|
||||||
|
matched))
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (any);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fuzzy match pattern against text, which is drawn into a region of the given
|
||||||
|
* display width. Returns a bitstr_t of width bits with a bit set for each
|
||||||
|
* column occupied by a matched character, or NULL if there is no match. A
|
||||||
|
* higher returned score is better.
|
||||||
|
*/
|
||||||
|
bitstr_t *
|
||||||
|
fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
|
||||||
|
{
|
||||||
|
struct fuzzy_char *cs;
|
||||||
|
char *matched = NULL, *best = NULL, *groupmatched;
|
||||||
|
struct utf8_data *tok;
|
||||||
|
bitstr_t *mask;
|
||||||
|
u_int ncs, i, j, column;
|
||||||
|
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||||
|
u_int start[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||||
|
u_int src[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||||
|
u_int vis[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||||
|
u_int wl, wc, wr, wa;
|
||||||
|
const char *cp, *sp;
|
||||||
|
int bestscore = 0, groupscore, found = 0, fold;
|
||||||
|
|
||||||
|
if (width == 0)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/* An empty query matches everything, with nothing highlighted. */
|
||||||
|
for (cp = pattern; *cp == ' ' || *cp == '|'; cp++)
|
||||||
|
/* nothing */;
|
||||||
|
if (*cp == '\0') {
|
||||||
|
if (score != NULL)
|
||||||
|
*score = 0;
|
||||||
|
return (bit_alloc(width));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smart-case: fold unless the pattern has an uppercase character. */
|
||||||
|
fold = 1;
|
||||||
|
for (cp = pattern; *cp != '\0'; cp++) {
|
||||||
|
if (*cp >= 'A' && *cp <= 'Z') {
|
||||||
|
fold = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan the text into visible characters. */
|
||||||
|
cs = fuzzy_scan(text, &ncs, widths);
|
||||||
|
matched = xcalloc(ncs == 0 ? 1 : ncs, sizeof *matched);
|
||||||
|
best = xcalloc(ncs == 0 ? 1 : ncs, sizeof *best);
|
||||||
|
tok = xreallocarray(NULL, strlen(pattern) + 1, sizeof *tok);
|
||||||
|
|
||||||
|
/* Match each |-separated group and keep the best-scoring one. */
|
||||||
|
cp = pattern;
|
||||||
|
while (*cp != '\0') {
|
||||||
|
while (*cp == ' ' || *cp == '|')
|
||||||
|
cp++;
|
||||||
|
if (*cp == '\0')
|
||||||
|
break;
|
||||||
|
sp = cp;
|
||||||
|
while (*cp != '\0' && *cp != '|')
|
||||||
|
cp++;
|
||||||
|
memset(matched, 0, ncs == 0 ? 1 : ncs);
|
||||||
|
groupmatched = matched;
|
||||||
|
if (fuzzy_match_group(sp, cp, tok, cs, ncs, fold,
|
||||||
|
&groupscore, groupmatched)) {
|
||||||
|
if (!found || groupscore > bestscore) {
|
||||||
|
found = 1;
|
||||||
|
bestscore = groupscore;
|
||||||
|
memcpy(best, matched, ncs == 0 ? 1 : ncs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tok);
|
||||||
|
if (!found) {
|
||||||
|
free(best);
|
||||||
|
free(matched);
|
||||||
|
free(cs);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Work out the trimmed widths and start columns of each alignment,
|
||||||
|
* mirroring format_draw_none.
|
||||||
|
*/
|
||||||
|
wl = widths[STYLE_ALIGN_LEFT];
|
||||||
|
wc = widths[STYLE_ALIGN_CENTRE];
|
||||||
|
wr = widths[STYLE_ALIGN_RIGHT];
|
||||||
|
wa = widths[STYLE_ALIGN_ABSOLUTE_CENTRE];
|
||||||
|
while (wl + wc + wr > width) {
|
||||||
|
if (wc > 0)
|
||||||
|
wc--;
|
||||||
|
else if (wr > 0)
|
||||||
|
wr--;
|
||||||
|
else
|
||||||
|
wl--;
|
||||||
|
}
|
||||||
|
if (wa > width)
|
||||||
|
wa = width;
|
||||||
|
|
||||||
|
start[STYLE_ALIGN_LEFT] = 0;
|
||||||
|
src[STYLE_ALIGN_LEFT] = 0;
|
||||||
|
vis[STYLE_ALIGN_LEFT] = wl;
|
||||||
|
|
||||||
|
start[STYLE_ALIGN_RIGHT] = width - wr;
|
||||||
|
src[STYLE_ALIGN_RIGHT] = widths[STYLE_ALIGN_RIGHT] - wr;
|
||||||
|
vis[STYLE_ALIGN_RIGHT] = wr;
|
||||||
|
|
||||||
|
start[STYLE_ALIGN_CENTRE] =
|
||||||
|
wl + ((width - wr) - wl) / 2 - wc / 2;
|
||||||
|
src[STYLE_ALIGN_CENTRE] = widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2;
|
||||||
|
vis[STYLE_ALIGN_CENTRE] = wc;
|
||||||
|
|
||||||
|
start[STYLE_ALIGN_ABSOLUTE_CENTRE] = (width - wa) / 2;
|
||||||
|
src[STYLE_ALIGN_ABSOLUTE_CENTRE] = 0;
|
||||||
|
vis[STYLE_ALIGN_ABSOLUTE_CENTRE] = wa;
|
||||||
|
|
||||||
|
/* Set a bit for each column of each matched character. */
|
||||||
|
mask = bit_alloc(width);
|
||||||
|
for (i = 0; i < ncs; i++) {
|
||||||
|
if (!best[i])
|
||||||
|
continue;
|
||||||
|
if (fuzzy_column(&cs[i], start, src, vis, &column) != 0)
|
||||||
|
continue;
|
||||||
|
for (j = 0; j < cs[i].width && column + j < width; j++)
|
||||||
|
bit_set(mask, column + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(best);
|
||||||
|
free(matched);
|
||||||
|
free(cs);
|
||||||
|
|
||||||
|
if (score != NULL)
|
||||||
|
*score = (bestscore < 0) ? 0 : (u_int)bestscore;
|
||||||
|
return (mask);
|
||||||
|
}
|
||||||
227
grid.c
227
grid.c
@@ -18,6 +18,9 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -56,6 +59,62 @@ static const struct grid_cell_entry grid_cleared_entry = {
|
|||||||
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
static void
|
||||||
|
grid_check_lines(struct grid *gd)
|
||||||
|
{
|
||||||
|
u_int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < gd->hsize + gd->sy; i++) {
|
||||||
|
for (j = i + 1; j < gd->hsize + gd->sy; j++) {
|
||||||
|
if (gd->linedata[i].celldata != NULL)
|
||||||
|
assert(gd->linedata[i].celldata != gd->linedata[j].celldata);
|
||||||
|
if (gd->linedata[i].extddata != NULL)
|
||||||
|
assert(gd->linedata[i].extddata != gd->linedata[j].extddata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grid_check_is_clear(struct grid *gd)
|
||||||
|
{
|
||||||
|
struct grid_line *gl;
|
||||||
|
u_int yy, ny;
|
||||||
|
|
||||||
|
assert(gd != NULL);
|
||||||
|
|
||||||
|
if (gd->sy == 0) {
|
||||||
|
assert(gd->linedata == NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(gd->linedata != NULL);
|
||||||
|
|
||||||
|
ny = gd->hsize + gd->sy;
|
||||||
|
for (yy = 0; yy < ny; yy++) {
|
||||||
|
gl = &gd->linedata[yy];
|
||||||
|
|
||||||
|
assert(gl->celldata == NULL);
|
||||||
|
assert(gl->cellused == 0);
|
||||||
|
assert(gl->cellsize == 0);
|
||||||
|
assert(gl->extddata == NULL);
|
||||||
|
assert(gl->extdsize == 0);
|
||||||
|
assert(gl->flags == 0);
|
||||||
|
assert(gl->time == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void
|
||||||
|
grid_check_lines(__unused struct grid *gd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grid_check_is_clear(__unused struct grid *gd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Store cell in entry. */
|
/* Store cell in entry. */
|
||||||
static void
|
static void
|
||||||
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
|
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
|
||||||
@@ -86,7 +145,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
|
|||||||
return (1);
|
return (1);
|
||||||
if (gc->data.size > 1 || gc->data.width > 1)
|
if (gc->data.size > 1 || gc->data.width > 1)
|
||||||
return (1);
|
return (1);
|
||||||
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
|
if ((gc->fg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) ||
|
||||||
|
(gc->bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)))
|
||||||
return (1);
|
return (1);
|
||||||
if (gc->us != 8) /* only supports 256 or RGB */
|
if (gc->us != 8) /* only supports 256 or RGB */
|
||||||
return (1);
|
return (1);
|
||||||
@@ -218,7 +278,7 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
|
|||||||
if (bg != 8)
|
if (bg != 8)
|
||||||
gee->bg = bg;
|
gee->bg = bg;
|
||||||
} else if (bg != 8) {
|
} else if (bg != 8) {
|
||||||
if (bg & COLOUR_FLAG_RGB) {
|
if (bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) {
|
||||||
grid_get_extended_cell(gl, gce, gce->flags);
|
grid_get_extended_cell(gl, gce, gce->flags);
|
||||||
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
|
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
|
||||||
gee->bg = bg;
|
gee->bg = bg;
|
||||||
@@ -286,9 +346,17 @@ grid_set_tab(struct grid_cell *gc, u_int width)
|
|||||||
static void
|
static void
|
||||||
grid_free_line(struct grid *gd, u_int py)
|
grid_free_line(struct grid *gd, u_int py)
|
||||||
{
|
{
|
||||||
free(gd->linedata[py].celldata);
|
struct grid_line *gl = &gd->linedata[py];
|
||||||
free(gd->linedata[py].extddata);
|
|
||||||
memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
|
#ifdef __APPLE__
|
||||||
|
assert(gl->cellused <= gl->cellsize);
|
||||||
|
assert(gl->extdsize == 0 || gl->extddata != NULL);
|
||||||
|
assert(gl->cellsize == 0 || gl->celldata != NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free(gl->celldata);
|
||||||
|
free(gl->extddata);
|
||||||
|
memset(gl, 0, sizeof *gl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free several lines. */
|
/* Free several lines. */
|
||||||
@@ -307,28 +375,18 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
|
|||||||
{
|
{
|
||||||
struct grid *gd;
|
struct grid *gd;
|
||||||
|
|
||||||
gd = xmalloc(sizeof *gd);
|
gd = xcalloc(1, sizeof *gd);
|
||||||
gd->sx = sx;
|
gd->sx = sx;
|
||||||
gd->sy = sy;
|
gd->sy = sy;
|
||||||
|
|
||||||
if (hlimit != 0)
|
if (hlimit != 0)
|
||||||
gd->flags = GRID_HISTORY;
|
gd->flags = GRID_HISTORY;
|
||||||
else
|
|
||||||
gd->flags = 0;
|
|
||||||
|
|
||||||
gd->hscrolled = 0;
|
|
||||||
gd->hsize = 0;
|
|
||||||
gd->hlimit = hlimit;
|
gd->hlimit = hlimit;
|
||||||
|
|
||||||
gd->scroll_added = 0;
|
|
||||||
gd->scroll_collected = 0;
|
|
||||||
gd->scroll_generation = 0;
|
|
||||||
|
|
||||||
if (gd->sy != 0)
|
if (gd->sy != 0)
|
||||||
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
|
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
|
||||||
else
|
|
||||||
gd->linedata = NULL;
|
|
||||||
|
|
||||||
|
grid_check_is_clear(gd);
|
||||||
return (gd);
|
return (gd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +506,8 @@ grid_scroll_history(struct grid *gd, u_int bg)
|
|||||||
gd->linedata[gd->hsize].time = current_time;
|
gd->linedata[gd->hsize].time = current_time;
|
||||||
gd->hsize++;
|
gd->hsize++;
|
||||||
gd->scroll_added++;
|
gd->scroll_added++;
|
||||||
|
|
||||||
|
grid_check_lines(gd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the history. */
|
/* Clear the history. */
|
||||||
@@ -497,6 +557,8 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
|
|||||||
gd->hscrolled++;
|
gd->hscrolled++;
|
||||||
gd->hsize++;
|
gd->hsize++;
|
||||||
gd->scroll_added++;
|
gd->scroll_added++;
|
||||||
|
|
||||||
|
grid_check_lines(gd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expand line to fit to cell. */
|
/* Expand line to fit to cell. */
|
||||||
@@ -758,6 +820,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
|
|||||||
}
|
}
|
||||||
if (py != 0 && (py < dy || py >= dy + ny))
|
if (py != 0 && (py < dy || py >= dy + ny))
|
||||||
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
|
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
|
||||||
|
|
||||||
|
grid_check_lines(gd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move a group of cells. */
|
/* Move a group of cells. */
|
||||||
@@ -796,9 +860,16 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values)
|
|||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
u_char r, g, b;
|
u_char r, g, b;
|
||||||
|
int c;
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
if (gc->fg & COLOUR_FLAG_256) {
|
if (gc->fg & COLOUR_FLAG_THEME) {
|
||||||
|
c = colour_theme_terminal_colour(gc->fg & 0xff);
|
||||||
|
if (c == 8)
|
||||||
|
values[n++] = 39;
|
||||||
|
else
|
||||||
|
values[n++] = c + 30;
|
||||||
|
} else if (gc->fg & COLOUR_FLAG_256) {
|
||||||
values[n++] = 38;
|
values[n++] = 38;
|
||||||
values[n++] = 5;
|
values[n++] = 5;
|
||||||
values[n++] = gc->fg & 0xff;
|
values[n++] = gc->fg & 0xff;
|
||||||
@@ -845,9 +916,16 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values)
|
|||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
u_char r, g, b;
|
u_char r, g, b;
|
||||||
|
int c;
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
if (gc->bg & COLOUR_FLAG_256) {
|
if (gc->bg & COLOUR_FLAG_THEME) {
|
||||||
|
c = colour_theme_terminal_colour(gc->bg & 0xff);
|
||||||
|
if (c == 8)
|
||||||
|
values[n++] = 49;
|
||||||
|
else
|
||||||
|
values[n++] = c + 40;
|
||||||
|
} else if (gc->bg & COLOUR_FLAG_256) {
|
||||||
values[n++] = 48;
|
values[n++] = 48;
|
||||||
values[n++] = 5;
|
values[n++] = 5;
|
||||||
values[n++] = gc->bg & 0xff;
|
values[n++] = gc->bg & 0xff;
|
||||||
@@ -894,9 +972,19 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
|
|||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
u_char r, g, b;
|
u_char r, g, b;
|
||||||
|
int c;
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
if (gc->us & COLOUR_FLAG_256) {
|
if (gc->us & COLOUR_FLAG_THEME) {
|
||||||
|
c = colour_theme_terminal_colour(gc->us & 0xff);
|
||||||
|
if (c == 8)
|
||||||
|
values[n++] = 59;
|
||||||
|
else {
|
||||||
|
values[n++] = 58;
|
||||||
|
values[n++] = 5;
|
||||||
|
values[n++] = c;
|
||||||
|
}
|
||||||
|
} else if (gc->us & COLOUR_FLAG_256) {
|
||||||
values[n++] = 58;
|
values[n++] = 58;
|
||||||
values[n++] = 5;
|
values[n++] = 5;
|
||||||
values[n++] = gc->us & 0xff;
|
values[n++] = gc->us & 0xff;
|
||||||
@@ -1223,6 +1311,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
|||||||
sy++;
|
sy++;
|
||||||
dy++;
|
dy++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grid_check_lines(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mark line as dead. */
|
/* Mark line as dead. */
|
||||||
@@ -1519,6 +1609,8 @@ grid_reflow(struct grid *gd, u_int sx)
|
|||||||
gd->linedata = target->linedata;
|
gd->linedata = target->linedata;
|
||||||
free(target);
|
free(target);
|
||||||
gd->scroll_generation++;
|
gd->scroll_generation++;
|
||||||
|
|
||||||
|
grid_check_lines(gd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert to position based on wrapped lines. */
|
/* Convert to position based on wrapped lines. */
|
||||||
@@ -1620,3 +1712,98 @@ grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
|
|||||||
return (0);
|
return (0);
|
||||||
return (utf8_cstrhas(set, &gc.data));
|
return (utf8_cstrhas(set, &gc.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Line flags to string. */
|
||||||
|
const char *
|
||||||
|
grid_line_flags_string(int flags)
|
||||||
|
{
|
||||||
|
static char s[128];
|
||||||
|
|
||||||
|
*s = '\0';
|
||||||
|
if (flags & GRID_LINE_WRAPPED)
|
||||||
|
strlcat(s, "WRAPPED,", sizeof s);
|
||||||
|
if (flags & GRID_LINE_EXTENDED)
|
||||||
|
strlcat(s, "EXTENDED,", sizeof s);
|
||||||
|
if (flags & GRID_LINE_DEAD)
|
||||||
|
strlcat(s, "DEAD,", sizeof s);
|
||||||
|
if (flags & GRID_LINE_START_PROMPT)
|
||||||
|
strlcat(s, "START_PROMPT,", sizeof s);
|
||||||
|
if (flags & GRID_LINE_START_OUTPUT)
|
||||||
|
strlcat(s, "START_OUTPUT,", sizeof s);
|
||||||
|
if (flags & GRID_LINE_HYPERLINK)
|
||||||
|
strlcat(s, "HYPERLINK,", sizeof s);
|
||||||
|
if (*s == '\0')
|
||||||
|
return ("NONE");
|
||||||
|
s[strlen(s) - 1] = '\0';
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cell flags to string. */
|
||||||
|
const char *
|
||||||
|
grid_cell_flags_string(int flags)
|
||||||
|
{
|
||||||
|
static char s[128];
|
||||||
|
|
||||||
|
*s = '\0';
|
||||||
|
if (flags & GRID_FLAG_FG256)
|
||||||
|
strlcat(s, "FG256,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_BG256)
|
||||||
|
strlcat(s, "BG256,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_PADDING)
|
||||||
|
strlcat(s, "PADDING,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_EXTENDED)
|
||||||
|
strlcat(s, "EXTENDED,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_SELECTED)
|
||||||
|
strlcat(s, "SELECTED,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_CLEARED)
|
||||||
|
strlcat(s, "CLEARED,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_TAB)
|
||||||
|
strlcat(s, "TAB,", sizeof s);
|
||||||
|
if (flags & GRID_FLAG_NOPALETTE)
|
||||||
|
strlcat(s, "NOPALETTE,", sizeof s);
|
||||||
|
if (*s == '\0')
|
||||||
|
return ("NONE");
|
||||||
|
s[strlen(s) - 1] = '\0';
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cell attributes to string. */
|
||||||
|
const char *
|
||||||
|
grid_cell_attr_string(int attr)
|
||||||
|
{
|
||||||
|
static char s[256];
|
||||||
|
|
||||||
|
*s = '\0';
|
||||||
|
if (attr & GRID_ATTR_CHARSET)
|
||||||
|
strlcat(s, "CHARSET,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_BRIGHT)
|
||||||
|
strlcat(s, "BRIGHT,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_DIM)
|
||||||
|
strlcat(s, "DIM,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_UNDERSCORE)
|
||||||
|
strlcat(s, "UNDERSCORE,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_BLINK)
|
||||||
|
strlcat(s, "BLINK,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_REVERSE)
|
||||||
|
strlcat(s, "REVERSE,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_HIDDEN)
|
||||||
|
strlcat(s, "HIDDEN,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_ITALICS)
|
||||||
|
strlcat(s, "ITALICS,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_STRIKETHROUGH)
|
||||||
|
strlcat(s, "STRIKETHROUGH,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_UNDERSCORE_2)
|
||||||
|
strlcat(s, "UNDERSCORE_2,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_UNDERSCORE_3)
|
||||||
|
strlcat(s, "UNDERSCORE_3,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_UNDERSCORE_4)
|
||||||
|
strlcat(s, "UNDERSCORE_4,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_UNDERSCORE_5)
|
||||||
|
strlcat(s, "UNDERSCORE_5,", sizeof s);
|
||||||
|
if (attr & GRID_ATTR_OVERLINE)
|
||||||
|
strlcat(s, "OVERLINE,", sizeof s);
|
||||||
|
if (*s == '\0')
|
||||||
|
return ("NONE");
|
||||||
|
s[strlen(s) - 1] = '\0';
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define MAX_HYPERLINKS 5000
|
#define MAX_HYPERLINKS 5000
|
||||||
|
#define MAX_HYPERLINK_URI 1024
|
||||||
|
|
||||||
static long long hyperlinks_next_external_id = 1;
|
static long long hyperlinks_next_external_id = 1;
|
||||||
static u_int global_hyperlinks_count;
|
static u_int global_hyperlinks_count;
|
||||||
@@ -145,9 +146,13 @@ hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
|
|||||||
internal_id_in = "";
|
internal_id_in = "";
|
||||||
|
|
||||||
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
|
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
|
||||||
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
|
if (strlen(uri) > MAX_HYPERLINK_URI) {
|
||||||
|
free(uri);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
if (*internal_id_in != '\0') {
|
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
|
||||||
|
if (*internal_id != '\0') {
|
||||||
find.uri = uri;
|
find.uri = uri;
|
||||||
find.internal_id = internal_id;
|
find.internal_id = internal_id;
|
||||||
|
|
||||||
|
|||||||
@@ -586,7 +586,7 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
|||||||
len = 8192;
|
len = 8192;
|
||||||
buf = xmalloc(len);
|
buf = xmalloc(len);
|
||||||
|
|
||||||
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
|
tmplen = xsnprintf(tmp, sizeof tmp, "\033P9;%uq", si->p2);
|
||||||
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
||||||
|
|
||||||
if (si->set_ra) {
|
if (si->set_ra) {
|
||||||
|
|||||||
14
input.c
14
input.c
@@ -1953,16 +1953,14 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
|
|||||||
case 2004:
|
case 2004:
|
||||||
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
||||||
break;
|
break;
|
||||||
|
case 2026:
|
||||||
|
screen_write_stop_sync(ictx->wp);
|
||||||
|
break;
|
||||||
case 2031:
|
case 2031:
|
||||||
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
||||||
if (ictx->wp != NULL)
|
if (ictx->wp != NULL)
|
||||||
ictx->wp->flags &= ~PANE_THEMECHANGED;
|
ictx->wp->flags &= ~PANE_THEMECHANGED;
|
||||||
break;
|
break;
|
||||||
case 2026: /* synchronized output */
|
|
||||||
screen_write_stop_sync(ictx->wp);
|
|
||||||
if (ictx->wp != NULL)
|
|
||||||
ictx->wp->flags |= PANE_REDRAW;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||||
break;
|
break;
|
||||||
@@ -2065,7 +2063,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
|||||||
ictx->wp->flags &= ~PANE_THEMECHANGED;
|
ictx->wp->flags &= ~PANE_THEMECHANGED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2026: /* synchronized output */
|
case 2026:
|
||||||
screen_write_start_sync(ictx->wp);
|
screen_write_start_sync(ictx->wp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -2822,10 +2820,10 @@ input_exit_rename(struct input_ctx *ictx)
|
|||||||
if (o != NULL)
|
if (o != NULL)
|
||||||
options_remove_or_default(o, -1, NULL);
|
options_remove_or_default(o, -1, NULL);
|
||||||
if (!options_get_number(w->options, "automatic-rename"))
|
if (!options_get_number(w->options, "automatic-rename"))
|
||||||
window_set_name(w, "", WINDOW_NAME_FORBID_EXT);
|
window_set_name(w, "", 1);
|
||||||
} else {
|
} else {
|
||||||
options_set_number(w->options, "automatic-rename", 0);
|
options_set_number(w->options, "automatic-rename", 0);
|
||||||
window_set_name(w, ictx->input_buf, WINDOW_NAME_FORBID_EXT);
|
window_set_name(w, ictx->input_buf, 1);
|
||||||
}
|
}
|
||||||
server_redraw_window_borders(w);
|
server_redraw_window_borders(w);
|
||||||
server_status_window(w);
|
server_status_window(w);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
" ''" \
|
" ''" \
|
||||||
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
|
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
|
||||||
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
|
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
|
||||||
" '#{?#{!:#{pane_floating_flag}},#{?pane_marked_set,,-}Swap Marked,}' 's' {swap-pane}" \
|
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
|
||||||
" ''" \
|
" ''" \
|
||||||
" 'Kill' 'X' {kill-pane}" \
|
" 'Kill' 'X' {kill-pane}" \
|
||||||
" 'Respawn' 'R' {respawn-pane -k}" \
|
" 'Respawn' 'R' {respawn-pane -k}" \
|
||||||
@@ -350,6 +350,8 @@ key_bindings_init(void)
|
|||||||
{
|
{
|
||||||
static const char *const defaults[] = {
|
static const char *const defaults[] = {
|
||||||
/* Prefix keys. */
|
/* Prefix keys. */
|
||||||
|
"bind -N 'Hide pane' _ { hide-pane }",
|
||||||
|
|
||||||
"bind -N 'Send the prefix key' C-b { send-prefix }",
|
"bind -N 'Send the prefix key' C-b { send-prefix }",
|
||||||
"bind -N 'Rotate through the panes' C-o { rotate-window }",
|
"bind -N 'Rotate through the panes' C-o { rotate-window }",
|
||||||
"bind -N 'Suspend the current client' C-z { suspend-client }",
|
"bind -N 'Suspend the current client' C-z { suspend-client }",
|
||||||
@@ -362,6 +364,7 @@ key_bindings_init(void)
|
|||||||
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
|
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
|
||||||
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
|
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
|
||||||
"bind -N 'New floating pane' * { new-pane }",
|
"bind -N 'New floating pane' * { new-pane }",
|
||||||
|
"bind -N 'Toggle pane between floating and tiled' @ { if -F '#{pane_floating_flag}' { join-pane } { break-pane -W } }",
|
||||||
"bind -N 'Switch to previous client' ( { switch-client -p }",
|
"bind -N 'Switch to previous client' ( { switch-client -p }",
|
||||||
"bind -N 'Switch to next client' ) { switch-client -n }",
|
"bind -N 'Switch to next client' ) { switch-client -n }",
|
||||||
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
|
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
|
||||||
@@ -402,6 +405,8 @@ key_bindings_init(void)
|
|||||||
"bind -N 'Redraw the current client' r { refresh-client }",
|
"bind -N 'Redraw the current client' r { refresh-client }",
|
||||||
"bind -N 'Choose a session from a list' s { choose-tree -Zs }",
|
"bind -N 'Choose a session from a list' s { choose-tree -Zs }",
|
||||||
"bind -N 'Show a clock' t { clock-mode }",
|
"bind -N 'Show a clock' t { clock-mode }",
|
||||||
|
"bind -N 'Switch to a window' Tab { new-pane -E -x75% -y30% -X0 -Y0; move-pane -P bottom-centre; switch-mode -wk }",
|
||||||
|
"bind -N 'Switch to a session' BTab { new-pane -E -x75% -y30% -X0 -Y0; move-pane -P bottom-centre; switch-mode -sk }",
|
||||||
"bind -N 'Choose a window from a list' w { choose-tree -Zw }",
|
"bind -N 'Choose a window from a list' w { choose-tree -Zw }",
|
||||||
"bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }",
|
"bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }",
|
||||||
"bind -N 'Zoom the active pane' z { resize-pane -Z }",
|
"bind -N 'Zoom the active pane' z { resize-pane -Z }",
|
||||||
@@ -471,7 +476,7 @@ key_bindings_init(void)
|
|||||||
"bind -n M-MouseDrag1Border { move-pane -M }",
|
"bind -n M-MouseDrag1Border { move-pane -M }",
|
||||||
|
|
||||||
/* Mouse button 1 down on status line. */
|
/* Mouse button 1 down on status line. */
|
||||||
"bind -n MouseDown1Status { switch-client -t= }",
|
"bind -n MouseDown1Status { if -F '#{&&:#{pane_active},#{!#{pane_hidden_flag}}}' { hide-pane -t= } { switch-client -t= } }",
|
||||||
"bind -n C-MouseDown1Status { swap-window -t@ }",
|
"bind -n C-MouseDown1Status { swap-window -t@ }",
|
||||||
|
|
||||||
/* Mouse button 1 down on default pane-border-format */
|
/* Mouse button 1 down on default pane-border-format */
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
|
|||||||
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
|
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
|
||||||
|
|
||||||
/* Destroy the old layout and swap to the new. */
|
/* Destroy the old layout and swap to the new. */
|
||||||
layout_free_cell(w->layout_root);
|
layout_free_cell(w->layout_root, 0);
|
||||||
w->layout_root = tiled_lc;
|
w->layout_root = tiled_lc;
|
||||||
|
|
||||||
/* Assign the panes into the cells. */
|
/* Assign the panes into the cells. */
|
||||||
@@ -291,7 +291,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
layout_free_cell(tiled_lc);
|
layout_free_cell(tiled_lc, 0);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,6 +423,6 @@ layout_construct(struct layout_cell *lcparent, const char **layout,
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
layout_free_cell(*lc);
|
layout_free_cell(*lc, 0);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|||||||
314
layout-set.c
314
layout-set.c
@@ -124,12 +124,12 @@ layout_set_previous(struct window *w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct window_pane *
|
static struct window_pane *
|
||||||
layout_first_tiled(struct window *w)
|
layout_set_first_tiled(struct window *w)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (!window_pane_is_floating(wp))
|
if (wp->layout_cell && layout_cell_is_tiled(wp->layout_cell))
|
||||||
return (wp);
|
return (wp);
|
||||||
}
|
}
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@@ -139,19 +139,15 @@ static void
|
|||||||
layout_set_even(struct window *w, enum layout_type type)
|
layout_set_even(struct window *w, enum layout_type type)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct layout_cell *lc, *lcnew;
|
struct layout_cell *lcroot, *lcchild;
|
||||||
u_int n, sx, sy;
|
u_int n, sx, sy;
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
/* Get number of panes. */
|
|
||||||
n = window_count_panes(w, 0);
|
n = window_count_panes(w, 0);
|
||||||
if (n <= 1)
|
if (n <= 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Free the old root and construct a new. */
|
|
||||||
layout_free(w);
|
|
||||||
lc = w->layout_root = layout_create_cell(NULL);
|
|
||||||
if (type == LAYOUT_LEFTRIGHT) {
|
if (type == LAYOUT_LEFTRIGHT) {
|
||||||
sx = (n * (PANE_MINIMUM + 1)) - 1;
|
sx = (n * (PANE_MINIMUM + 1)) - 1;
|
||||||
if (sx < w->sx)
|
if (sx < w->sx)
|
||||||
@@ -163,30 +159,30 @@ layout_set_even(struct window *w, enum layout_type type)
|
|||||||
sy = w->sy;
|
sy = w->sy;
|
||||||
sx = w->sx;
|
sx = w->sx;
|
||||||
}
|
}
|
||||||
layout_set_size(lc, sx, sy, 0, 0);
|
|
||||||
layout_make_node(lc, type);
|
|
||||||
|
|
||||||
/* Build new leaf cells. */
|
layout_free(w, 1);
|
||||||
|
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||||
|
layout_set_size(lcroot, sx, sy, 0, 0);
|
||||||
|
layout_make_node(lcroot, type);
|
||||||
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (window_pane_is_floating(wp))
|
lcchild = wp->layout_cell;
|
||||||
continue;
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
|
||||||
lcnew = layout_create_cell(lc);
|
lcchild->parent = lcroot;
|
||||||
layout_make_leaf(lcnew, wp);
|
if (layout_cell_is_tiled(lcchild)) {
|
||||||
lcnew->sx = w->sx;
|
lcchild->sx = w->sx;
|
||||||
lcnew->sy = w->sy;
|
lcchild->sy = w->sy;
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Spread out cells. */
|
layout_spread_cell(w, lcroot);
|
||||||
layout_spread_cell(w, lc);
|
|
||||||
|
|
||||||
/* Fix cell offsets. */
|
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||||
notify_window("window-layout-changed", w);
|
notify_window("window-layout-changed", w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
@@ -206,15 +202,14 @@ layout_set_even_v(struct window *w)
|
|||||||
static void
|
static void
|
||||||
layout_set_main_h(struct window *w)
|
layout_set_main_h(struct window *w)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp, *wpmain;
|
||||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
|
||||||
u_int n, mainh, otherh, sx, sy;
|
u_int n, mainh, otherh, sx, sy;
|
||||||
char *cause;
|
char *cause;
|
||||||
const char *s;
|
const char *s;
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
/* Get number of panes. */
|
|
||||||
n = window_count_panes(w, 0);
|
n = window_count_panes(w, 0);
|
||||||
if (n <= 1)
|
if (n <= 1)
|
||||||
return;
|
return;
|
||||||
@@ -255,52 +250,48 @@ layout_set_main_h(struct window *w)
|
|||||||
if (sx < w->sx)
|
if (sx < w->sx)
|
||||||
sx = w->sx;
|
sx = w->sx;
|
||||||
|
|
||||||
/* Free old tree and create a new root. */
|
layout_free(w, 1);
|
||||||
layout_free(w);
|
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||||
lc = w->layout_root = layout_create_cell(NULL);
|
layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0);
|
||||||
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
|
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
|
||||||
layout_make_node(lc, LAYOUT_TOPBOTTOM);
|
|
||||||
|
|
||||||
/* Create the main pane. */
|
wpmain = layout_set_first_tiled(w);
|
||||||
lcmain = layout_create_cell(lc);
|
lcmain = wpmain->layout_cell;
|
||||||
|
lcmain->parent = lcroot;
|
||||||
layout_set_size(lcmain, sx, mainh, 0, 0);
|
layout_set_size(lcmain, sx, mainh, 0, 0);
|
||||||
layout_make_leaf(lcmain, layout_first_tiled(w));
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
|
||||||
|
|
||||||
/* Create the other pane. */
|
|
||||||
lcother = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcother, sx, otherh, 0, 0);
|
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
|
wp = TAILQ_NEXT(wpmain, entry);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
layout_make_leaf(lcother, wp);
|
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
wp->layout_cell->parent = lcroot;
|
||||||
} else {
|
} else {
|
||||||
|
lcother = layout_create_cell(lcroot);
|
||||||
|
layout_set_size(lcother, sx, otherh, 0, 0);
|
||||||
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry);
|
||||||
|
|
||||||
/* Add the remaining panes as children. */
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (window_pane_is_floating(wp))
|
if (wp == wpmain)
|
||||||
continue;
|
continue;
|
||||||
if (wp == layout_first_tiled(w))
|
lcchild = wp->layout_cell;
|
||||||
continue;
|
|
||||||
lcchild = layout_create_cell(lcother);
|
|
||||||
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
|
|
||||||
layout_make_leaf(lcchild, wp);
|
|
||||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||||
|
lcchild->parent = lcother;
|
||||||
|
if (layout_cell_is_tiled(lcchild))
|
||||||
|
layout_set_size(lcchild, PANE_MINIMUM, otherh,
|
||||||
|
0, 0);
|
||||||
}
|
}
|
||||||
layout_spread_cell(w, lcother);
|
layout_spread_cell(w, lcother);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix cell offsets. */
|
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||||
notify_window("window-layout-changed", w);
|
notify_window("window-layout-changed", w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
@@ -308,15 +299,14 @@ layout_set_main_h(struct window *w)
|
|||||||
static void
|
static void
|
||||||
layout_set_main_h_mirrored(struct window *w)
|
layout_set_main_h_mirrored(struct window *w)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp, *wpmain;
|
||||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
|
||||||
u_int n, mainh, otherh, sx, sy;
|
u_int n, mainh, otherh, sx, sy;
|
||||||
char *cause;
|
char *cause;
|
||||||
const char *s;
|
const char *s;
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
/* Get number of panes. */
|
|
||||||
n = window_count_panes(w, 0);
|
n = window_count_panes(w, 0);
|
||||||
if (n <= 1)
|
if (n <= 1)
|
||||||
return;
|
return;
|
||||||
@@ -357,52 +347,48 @@ layout_set_main_h_mirrored(struct window *w)
|
|||||||
if (sx < w->sx)
|
if (sx < w->sx)
|
||||||
sx = w->sx;
|
sx = w->sx;
|
||||||
|
|
||||||
/* Free old tree and create a new root. */
|
layout_free(w, 1);
|
||||||
layout_free(w);
|
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||||
lc = w->layout_root = layout_create_cell(NULL);
|
layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0);
|
||||||
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
|
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
|
||||||
layout_make_node(lc, LAYOUT_TOPBOTTOM);
|
|
||||||
|
wpmain = layout_set_first_tiled(w);
|
||||||
|
lcmain = wpmain->layout_cell;
|
||||||
|
lcmain->parent = lcroot;
|
||||||
|
layout_set_size(lcmain, sx, mainh, 0, 0);
|
||||||
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||||
|
|
||||||
/* Create the other pane. */
|
|
||||||
lcother = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcother, sx, otherh, 0, 0);
|
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
|
wp = TAILQ_NEXT(wpmain, entry);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
layout_make_leaf(lcother, wp);
|
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
wp->layout_cell->parent = lcroot;
|
||||||
} else {
|
} else {
|
||||||
|
lcother = layout_create_cell(lcroot);
|
||||||
|
layout_set_size(lcother, sx, otherh, 0, 0);
|
||||||
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry);
|
||||||
|
|
||||||
/* Add the remaining panes as children. */
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (window_pane_is_floating(wp))
|
if (wp == wpmain)
|
||||||
continue;
|
continue;
|
||||||
if (wp == layout_first_tiled(w))
|
lcchild = wp->layout_cell;
|
||||||
continue;
|
|
||||||
lcchild = layout_create_cell(lcother);
|
|
||||||
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
|
|
||||||
layout_make_leaf(lcchild, wp);
|
|
||||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||||
|
lcchild->parent = lcother;
|
||||||
|
if (layout_cell_is_tiled(lcchild))
|
||||||
|
layout_set_size(lcchild, PANE_MINIMUM, otherh,
|
||||||
|
0, 0);
|
||||||
}
|
}
|
||||||
layout_spread_cell(w, lcother);
|
layout_spread_cell(w, lcother);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the main pane. */
|
|
||||||
lcmain = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcmain, sx, mainh, 0, 0);
|
|
||||||
layout_make_leaf(lcmain, layout_first_tiled(w));
|
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
|
||||||
|
|
||||||
/* Fix cell offsets. */
|
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||||
notify_window("window-layout-changed", w);
|
notify_window("window-layout-changed", w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
@@ -410,21 +396,20 @@ layout_set_main_h_mirrored(struct window *w)
|
|||||||
static void
|
static void
|
||||||
layout_set_main_v(struct window *w)
|
layout_set_main_v(struct window *w)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp, *wpmain;
|
||||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
|
||||||
u_int n, mainw, otherw, sx, sy;
|
u_int n, mainw, otherw, sx, sy;
|
||||||
char *cause;
|
char *cause;
|
||||||
const char *s;
|
const char *s;
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
/* Get number of panes. */
|
|
||||||
n = window_count_panes(w, 0);
|
n = window_count_panes(w, 0);
|
||||||
if (n <= 1)
|
if (n <= 1)
|
||||||
return;
|
return;
|
||||||
n--; /* take off main pane */
|
n--; /* take off main pane */
|
||||||
|
|
||||||
/* Find available width - take off one line for the border. */
|
/* Find available width - take off one column for the border. */
|
||||||
sx = w->sx - 1;
|
sx = w->sx - 1;
|
||||||
|
|
||||||
/* Get the main pane width. */
|
/* Get the main pane width. */
|
||||||
@@ -459,52 +444,48 @@ layout_set_main_v(struct window *w)
|
|||||||
if (sy < w->sy)
|
if (sy < w->sy)
|
||||||
sy = w->sy;
|
sy = w->sy;
|
||||||
|
|
||||||
/* Free old tree and create a new root. */
|
layout_free(w, 1);
|
||||||
layout_free(w);
|
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||||
lc = w->layout_root = layout_create_cell(NULL);
|
layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0);
|
||||||
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
|
layout_make_node(lcroot, LAYOUT_LEFTRIGHT);
|
||||||
layout_make_node(lc, LAYOUT_LEFTRIGHT);
|
|
||||||
|
|
||||||
/* Create the main pane. */
|
wpmain = layout_set_first_tiled(w);
|
||||||
lcmain = layout_create_cell(lc);
|
lcmain = wpmain->layout_cell;
|
||||||
|
lcmain->parent = lcroot;
|
||||||
layout_set_size(lcmain, mainw, sy, 0, 0);
|
layout_set_size(lcmain, mainw, sy, 0, 0);
|
||||||
layout_make_leaf(lcmain, layout_first_tiled(w));
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
|
||||||
|
|
||||||
/* Create the other pane. */
|
|
||||||
lcother = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcother, otherw, sy, 0, 0);
|
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
|
wp = TAILQ_NEXT(wpmain, entry);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
layout_make_leaf(lcother, wp);
|
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
wp->layout_cell->parent = lcroot;
|
||||||
} else {
|
} else {
|
||||||
|
lcother = layout_create_cell(lcroot);
|
||||||
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
layout_set_size(lcother, otherw, sy, 0, 0);
|
||||||
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry);
|
||||||
|
|
||||||
/* Add the remaining panes as children. */
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (window_pane_is_floating(wp))
|
if (wp == wpmain)
|
||||||
continue;
|
continue;
|
||||||
if (wp == layout_first_tiled(w))
|
lcchild = wp->layout_cell;
|
||||||
continue;
|
|
||||||
lcchild = layout_create_cell(lcother);
|
|
||||||
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
|
|
||||||
layout_make_leaf(lcchild, wp);
|
|
||||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||||
|
lcchild->parent = lcother;
|
||||||
|
if (layout_cell_is_tiled(lcchild))
|
||||||
|
layout_set_size(lcchild, otherw, PANE_MINIMUM,
|
||||||
|
0, 0);
|
||||||
}
|
}
|
||||||
layout_spread_cell(w, lcother);
|
layout_spread_cell(w, lcother);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix cell offsets. */
|
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||||
notify_window("window-layout-changed", w);
|
notify_window("window-layout-changed", w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
@@ -512,8 +493,8 @@ layout_set_main_v(struct window *w)
|
|||||||
static void
|
static void
|
||||||
layout_set_main_v_mirrored(struct window *w)
|
layout_set_main_v_mirrored(struct window *w)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp, *wpmain;
|
||||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
|
||||||
u_int n, mainw, otherw, sx, sy;
|
u_int n, mainw, otherw, sx, sy;
|
||||||
char *cause;
|
char *cause;
|
||||||
const char *s;
|
const char *s;
|
||||||
@@ -526,7 +507,7 @@ layout_set_main_v_mirrored(struct window *w)
|
|||||||
return;
|
return;
|
||||||
n--; /* take off main pane */
|
n--; /* take off main pane */
|
||||||
|
|
||||||
/* Find available width - take off one line for the border. */
|
/* Find available width - take off one column for the border. */
|
||||||
sx = w->sx - 1;
|
sx = w->sx - 1;
|
||||||
|
|
||||||
/* Get the main pane width. */
|
/* Get the main pane width. */
|
||||||
@@ -561,62 +542,58 @@ layout_set_main_v_mirrored(struct window *w)
|
|||||||
if (sy < w->sy)
|
if (sy < w->sy)
|
||||||
sy = w->sy;
|
sy = w->sy;
|
||||||
|
|
||||||
/* Free old tree and create a new root. */
|
layout_free(w, 1);
|
||||||
layout_free(w);
|
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||||
lc = w->layout_root = layout_create_cell(NULL);
|
layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0);
|
||||||
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
|
layout_make_node(lcroot, LAYOUT_LEFTRIGHT);
|
||||||
layout_make_node(lc, LAYOUT_LEFTRIGHT);
|
|
||||||
|
wpmain = layout_set_first_tiled(w);
|
||||||
|
lcmain = wpmain->layout_cell;
|
||||||
|
lcmain->parent = lcroot;
|
||||||
|
layout_set_size(lcmain, mainw, sy, 0, 0);
|
||||||
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||||
|
|
||||||
/* Create the other pane. */
|
|
||||||
lcother = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcother, otherw, sy, 0, 0);
|
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
|
wp = TAILQ_NEXT(wpmain, entry);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
layout_make_leaf(lcother, wp);
|
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
wp->layout_cell->parent = lcroot;
|
||||||
} else {
|
} else {
|
||||||
|
lcother = layout_create_cell(lcroot);
|
||||||
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
layout_set_size(lcother, otherw, sy, 0, 0);
|
||||||
|
TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry);
|
||||||
|
|
||||||
/* Add the remaining panes as children. */
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (window_pane_is_floating(wp))
|
if (wp == wpmain)
|
||||||
continue;
|
continue;
|
||||||
if (wp == layout_first_tiled(w))
|
lcchild = wp->layout_cell;
|
||||||
continue;
|
|
||||||
lcchild = layout_create_cell(lcother);
|
|
||||||
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
|
|
||||||
layout_make_leaf(lcchild, wp);
|
|
||||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||||
|
lcchild->parent = lcother;
|
||||||
|
if (layout_cell_is_tiled(lcchild))
|
||||||
|
layout_set_size(lcchild, otherw, PANE_MINIMUM,
|
||||||
|
0, 0);
|
||||||
}
|
}
|
||||||
layout_spread_cell(w, lcother);
|
layout_spread_cell(w, lcother);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the main pane. */
|
|
||||||
lcmain = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcmain, mainw, sy, 0, 0);
|
|
||||||
layout_make_leaf(lcmain, layout_first_tiled(w));
|
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
|
||||||
|
|
||||||
/* Fix cell offsets. */
|
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||||
notify_window("window-layout-changed", w);
|
notify_window("window-layout-changed", w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
layout_set_tiled(struct window *w)
|
layout_set_tiled(struct window *w)
|
||||||
{
|
{
|
||||||
struct options *oo = w->options;
|
struct options *oo = w->options;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct layout_cell *lc, *lcrow, *lcchild;
|
struct layout_cell *lcroot, *lcrow, *lcchild;
|
||||||
u_int n, width, height, used, sx, sy;
|
u_int n, width, height, used, sx, sy;
|
||||||
u_int i, j, columns, rows, max_columns;
|
u_int i, j, columns, rows, max_columns;
|
||||||
|
|
||||||
@@ -647,56 +624,59 @@ layout_set_tiled(struct window *w)
|
|||||||
if (height < PANE_MINIMUM)
|
if (height < PANE_MINIMUM)
|
||||||
height = PANE_MINIMUM;
|
height = PANE_MINIMUM;
|
||||||
|
|
||||||
/* Free old tree and create a new root. */
|
|
||||||
layout_free(w);
|
|
||||||
lc = w->layout_root = layout_create_cell(NULL);
|
|
||||||
sx = ((width + 1) * columns) - 1;
|
sx = ((width + 1) * columns) - 1;
|
||||||
if (sx < w->sx)
|
if (sx < w->sx)
|
||||||
sx = w->sx;
|
sx = w->sx;
|
||||||
sy = ((height + 1) * rows) - 1;
|
sy = ((height + 1) * rows) - 1;
|
||||||
if (sy < w->sy)
|
if (sy < w->sy)
|
||||||
sy = w->sy;
|
sy = w->sy;
|
||||||
layout_set_size(lc, sx, sy, 0, 0);
|
|
||||||
layout_make_node(lc, LAYOUT_TOPBOTTOM);
|
|
||||||
|
|
||||||
/* Create a grid of the cells, skipping any floating panes. */
|
layout_free(w, 1);
|
||||||
|
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||||
|
layout_set_size(lcroot, sx, sy, 0, 0);
|
||||||
|
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
|
||||||
|
|
||||||
|
/* Create a grid of the tiled cells. */
|
||||||
wp = TAILQ_FIRST(&w->panes);
|
wp = TAILQ_FIRST(&w->panes);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
|
||||||
for (j = 0; j < rows; j++) {
|
for (j = 0; j < rows; j++) {
|
||||||
|
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||||
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
/* If this is the last cell, all done. */
|
/* If this is the last cell, all done. */
|
||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Create the new row. */
|
lcchild = wp->layout_cell;
|
||||||
lcrow = layout_create_cell(lc);
|
|
||||||
layout_set_size(lcrow, w->sx, height, 0, 0);
|
|
||||||
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
|
|
||||||
|
|
||||||
/* If only one column, just use the row directly. */
|
/* If only one column, just use the row directly. */
|
||||||
if (n - (j * columns) == 1 || columns == 1) {
|
if (n - (j * columns) == 1 || columns == 1) {
|
||||||
layout_make_leaf(lcrow, wp);
|
lcchild->parent = lcroot;
|
||||||
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
|
||||||
|
layout_set_size(lcchild, w->sx, height, 0, 0);
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add in the columns. */
|
/* Create the new row. */
|
||||||
|
lcrow = layout_create_cell(lcroot);
|
||||||
layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
|
layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
|
||||||
|
layout_set_size(lcrow, w->sx, height, 0, 0);
|
||||||
|
TAILQ_INSERT_TAIL(&lcroot->cells, lcrow, entry);
|
||||||
|
|
||||||
|
/* Add in the columns. */
|
||||||
for (i = 0; i < columns; i++) {
|
for (i = 0; i < columns; i++) {
|
||||||
/* Create and add a pane cell. */
|
/* Create and add a pane cell. */
|
||||||
lcchild = layout_create_cell(lcrow);
|
lcchild->parent = lcrow;
|
||||||
layout_set_size(lcchild, width, height, 0, 0);
|
|
||||||
layout_make_leaf(lcchild, wp);
|
|
||||||
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
|
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
|
||||||
|
layout_set_size(lcchild, width, height, 0, 0);
|
||||||
|
|
||||||
/* Move to the next non-floating cell. */
|
/* Move to the next non-floating cell. */
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
while (wp != NULL && window_pane_is_floating(wp))
|
while (wp != NULL &&
|
||||||
|
!layout_cell_is_tiled(wp->layout_cell))
|
||||||
wp = TAILQ_NEXT(wp, entry);
|
wp = TAILQ_NEXT(wp, entry);
|
||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
break;
|
break;
|
||||||
|
lcchild = wp->layout_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -713,21 +693,19 @@ layout_set_tiled(struct window *w)
|
|||||||
w->sx - used);
|
w->sx - used);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust the last row height to fit if necessary. */
|
|
||||||
used = (rows * height) + rows - 1;
|
used = (rows * height) + rows - 1;
|
||||||
if (w->sy > used) {
|
if (w->sy > used) {
|
||||||
lcrow = TAILQ_LAST(&lc->cells, layout_cells);
|
lcrow = TAILQ_LAST(&lcroot->cells, layout_cells);
|
||||||
layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
|
layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
|
||||||
w->sy - used);
|
w->sy - used);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix cell offsets. */
|
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
layout_print_cell(w->layout_root, __func__, 1);
|
layout_print_cell(w->layout_root, __func__, 1);
|
||||||
|
|
||||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||||
notify_window("window-layout-changed", w);
|
notify_window("window-layout-changed", w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
|
|||||||
424
layout.c
424
layout.c
@@ -31,8 +31,9 @@
|
|||||||
* a cell which contains a list of cells, and 'leaf' to refer to a cell that
|
* a cell which contains a list of cells, and 'leaf' to refer to a cell that
|
||||||
* contains a window pane. A leaf is considered to be 'tiled' if it is to be
|
* contains a window pane. A leaf is considered to be 'tiled' if it is to be
|
||||||
* drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also
|
* drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also
|
||||||
* tiled. A cell's 'split' size refers to the side that is shortened when
|
* tiled or a node that contains a tiled leaf in a subtree. A cell's 'split'
|
||||||
* splitting it, determined by the parent's type.
|
* size refers to the side that is shortened when splitting it, determined by
|
||||||
|
* the parent's type.
|
||||||
*
|
*
|
||||||
* Each window has a pointer to the root of its layout tree (containing its
|
* Each window has a pointer to the root of its layout tree (containing its
|
||||||
* panes), every pane has a pointer back to the cell containing it, and each
|
* panes), every pane has a pointer back to the cell containing it, and each
|
||||||
@@ -52,6 +53,7 @@ static u_int layout_new_pane_size(struct window *, u_int,
|
|||||||
u_int);
|
u_int);
|
||||||
static int layout_set_size_check(struct window *, struct layout_cell *,
|
static int layout_set_size_check(struct window *, struct layout_cell *,
|
||||||
enum layout_type, int);
|
enum layout_type, int);
|
||||||
|
static int layout_cell_has_tiled_child(struct layout_cell *);
|
||||||
static void layout_resize_child_cells(struct window *,
|
static void layout_resize_child_cells(struct window *,
|
||||||
struct layout_cell *);
|
struct layout_cell *);
|
||||||
|
|
||||||
@@ -74,6 +76,12 @@ layout_create_cell(struct layout_cell *lcparent)
|
|||||||
lc->xoff = INT_MAX;
|
lc->xoff = INT_MAX;
|
||||||
lc->yoff = INT_MAX;
|
lc->yoff = INT_MAX;
|
||||||
|
|
||||||
|
lc->saved_sx = UINT_MAX;
|
||||||
|
lc->saved_sy = UINT_MAX;
|
||||||
|
|
||||||
|
lc->saved_xoff = INT_MAX;
|
||||||
|
lc->saved_yoff = INT_MAX;
|
||||||
|
|
||||||
lc->wp = NULL;
|
lc->wp = NULL;
|
||||||
|
|
||||||
return (lc);
|
return (lc);
|
||||||
@@ -81,20 +89,24 @@ layout_create_cell(struct layout_cell *lcparent)
|
|||||||
|
|
||||||
/* Free a layout cell. */
|
/* Free a layout cell. */
|
||||||
void
|
void
|
||||||
layout_free_cell(struct layout_cell *lc)
|
layout_free_cell(struct layout_cell *lc, int only_nodes)
|
||||||
{
|
{
|
||||||
struct layout_cell *lcchild;
|
struct layout_cell *lcchild, *lcnext;
|
||||||
|
|
||||||
if (lc == NULL)
|
if (lc == NULL || (only_nodes && lc->type == LAYOUT_WINDOWPANE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (lc->type) {
|
switch (lc->type) {
|
||||||
case LAYOUT_LEFTRIGHT:
|
case LAYOUT_LEFTRIGHT:
|
||||||
case LAYOUT_TOPBOTTOM:
|
case LAYOUT_TOPBOTTOM:
|
||||||
while (!TAILQ_EMPTY(&lc->cells)) {
|
lcchild = TAILQ_FIRST(&lc->cells);
|
||||||
lcchild = TAILQ_FIRST(&lc->cells);
|
while (lcchild != NULL) {
|
||||||
TAILQ_REMOVE(&lc->cells, lcchild, entry);
|
lcnext = TAILQ_NEXT(lcchild, entry);
|
||||||
layout_free_cell(lcchild);
|
if (!only_nodes || lcchild->type != LAYOUT_WINDOWPANE) {
|
||||||
|
TAILQ_REMOVE(&lc->cells, lcchild, entry);
|
||||||
|
layout_free_cell(lcchild, only_nodes);
|
||||||
|
}
|
||||||
|
lcchild = lcnext;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LAYOUT_WINDOWPANE:
|
case LAYOUT_WINDOWPANE:
|
||||||
@@ -198,6 +210,16 @@ layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, int yoff)
|
|||||||
lc->yoff = yoff;
|
lc->yoff = yoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
layout_save_size(struct layout_cell *lc)
|
||||||
|
{
|
||||||
|
lc->saved_sx = lc->sx;
|
||||||
|
lc->saved_sy = lc->sy;
|
||||||
|
|
||||||
|
lc->saved_xoff = lc->xoff;
|
||||||
|
lc->saved_yoff = lc->yoff;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make a cell a leaf cell. */
|
/* Make a cell a leaf cell. */
|
||||||
void
|
void
|
||||||
layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
|
layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
|
||||||
@@ -248,7 +270,7 @@ layout_fix_zindexes(struct window *w, struct layout_cell *lc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
layout_cell_is_tiled(struct layout_cell *lc)
|
layout_cell_is_tiled(struct layout_cell *lc)
|
||||||
{
|
{
|
||||||
int is_leaf = lc->type == LAYOUT_WINDOWPANE;
|
int is_leaf = lc->type == LAYOUT_WINDOWPANE;
|
||||||
@@ -290,6 +312,27 @@ layout_cell_is_first_tiled(struct layout_cell *lc)
|
|||||||
return (lcchild == lc);
|
return (lcchild == lc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct layout_cell *
|
||||||
|
layout_cell_get_first_tiled(struct layout_cell *lc)
|
||||||
|
{
|
||||||
|
struct layout_cell *lcchild, *lcchild2;
|
||||||
|
|
||||||
|
if (layout_cell_is_tiled(lc))
|
||||||
|
return (lc);
|
||||||
|
if (lc->type == LAYOUT_WINDOWPANE)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
||||||
|
if (layout_cell_is_tiled(lcchild))
|
||||||
|
return (lcchild);
|
||||||
|
if (lcchild->type != LAYOUT_WINDOWPANE) {
|
||||||
|
lcchild2 = layout_cell_get_first_tiled(lcchild);
|
||||||
|
if (lcchild2 != NULL)
|
||||||
|
return (lcchild2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Fix cell offsets for a child cell. */
|
/* Fix cell offsets for a child cell. */
|
||||||
static void
|
static void
|
||||||
@@ -405,16 +448,15 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
|||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc;
|
||||||
int status, scrollbars, sb_pos, sb_w, sb_pad;
|
int status, sb_w, sb_pad;
|
||||||
int old_xoff, old_yoff, changed = 0;
|
int old_xoff, old_yoff, changed = 0;
|
||||||
u_int sx, sy, old_sx, old_sy;
|
u_int sx, sy, old_sx, old_sy;
|
||||||
|
|
||||||
status = window_get_pane_status(w);
|
status = window_get_pane_status(w);
|
||||||
scrollbars = options_get_number(w->options, "pane-scrollbars");
|
|
||||||
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
|
|
||||||
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if ((lc = wp->layout_cell) == NULL || wp == skip)
|
if ((lc = wp->layout_cell) == NULL || wp == skip ||
|
||||||
|
!window_pane_is_visible(wp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
old_xoff = wp->xoff;
|
old_xoff = wp->xoff;
|
||||||
@@ -434,14 +476,14 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
|||||||
sy--;
|
sy--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window_pane_show_scrollbar(wp, scrollbars)) {
|
if (window_pane_scrollbar_reserve(wp)) {
|
||||||
sb_w = wp->scrollbar_style.width;
|
sb_w = wp->scrollbar_style.width;
|
||||||
sb_pad = wp->scrollbar_style.pad;
|
sb_pad = wp->scrollbar_style.pad;
|
||||||
if (sb_w < 1)
|
if (sb_w < 1)
|
||||||
sb_w = 1;
|
sb_w = 1;
|
||||||
if (sb_pad < 0)
|
if (sb_pad < 0)
|
||||||
sb_pad = 0;
|
sb_pad = 0;
|
||||||
if (sb_pos == PANE_SCROLLBARS_LEFT) {
|
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
|
||||||
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) {
|
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) {
|
||||||
wp->xoff = wp->xoff +
|
wp->xoff = wp->xoff +
|
||||||
(int)sx - PANE_MINIMUM;
|
(int)sx - PANE_MINIMUM;
|
||||||
@@ -498,16 +540,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
|
|||||||
struct layout_cell *lcchild;
|
struct layout_cell *lcchild;
|
||||||
struct style *sb_style = &w->active->scrollbar_style;
|
struct style *sb_style = &w->active->scrollbar_style;
|
||||||
u_int available, minimum;
|
u_int available, minimum;
|
||||||
int status, scrollbars;
|
int status;
|
||||||
|
|
||||||
status = window_get_pane_status(w);
|
status = window_get_pane_status(w);
|
||||||
scrollbars = options_get_number(w->options, "pane-scrollbars");
|
|
||||||
|
|
||||||
if (lc->type == LAYOUT_WINDOWPANE) {
|
if (lc->type == LAYOUT_WINDOWPANE) {
|
||||||
/* Space available in this cell only. */
|
/* Space available in this cell only. */
|
||||||
if (type == LAYOUT_LEFTRIGHT) {
|
if (type == LAYOUT_LEFTRIGHT) {
|
||||||
available = lc->sx;
|
available = lc->sx;
|
||||||
if (scrollbars)
|
if (w->sb == PANE_SCROLLBARS_ALWAYS)
|
||||||
minimum = PANE_MINIMUM + sb_style->width +
|
minimum = PANE_MINIMUM + sb_style->width +
|
||||||
sb_style->pad;
|
sb_style->pad;
|
||||||
else
|
else
|
||||||
@@ -603,6 +644,20 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Resizes a cell to a specified size */
|
||||||
|
void
|
||||||
|
layout_resize_set_size(struct window *w, struct layout_cell *lc,
|
||||||
|
enum layout_type type, u_int size)
|
||||||
|
{
|
||||||
|
int change;
|
||||||
|
|
||||||
|
if (type == LAYOUT_LEFTRIGHT)
|
||||||
|
change = size - lc->sx;
|
||||||
|
else
|
||||||
|
change = size - lc->sy;
|
||||||
|
layout_resize_adjust(w, lc, type, change);
|
||||||
|
}
|
||||||
|
|
||||||
/* Find and return the nearest neighbour to a cell in a specific direction. */
|
/* Find and return the nearest neighbour to a cell in a specific direction. */
|
||||||
static struct layout_cell *
|
static struct layout_cell *
|
||||||
layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction)
|
layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction)
|
||||||
@@ -655,18 +710,22 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
|
|||||||
struct layout_cell *lcother = NULL, *lcparent;
|
struct layout_cell *lcother = NULL, *lcparent;
|
||||||
int change;
|
int change;
|
||||||
|
|
||||||
/* If no parent, this is the last pane in a window. */
|
/*
|
||||||
|
* If no parent, this is either a floating pane or the last
|
||||||
|
* pane so window close is imminent and there is no need to
|
||||||
|
* resize anything.
|
||||||
|
*/
|
||||||
lcparent = lc->parent;
|
lcparent = lc->parent;
|
||||||
if (lcparent == NULL) {
|
if (lcparent == NULL) {
|
||||||
if (lc->wp != NULL)
|
if (lc->wp != NULL)
|
||||||
*lcroot = NULL;
|
*lcroot = NULL;
|
||||||
layout_free_cell(lc);
|
layout_free_cell(lc, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!layout_cell_is_tiled(lc)) {
|
if (!layout_cell_is_tiled(lc)) {
|
||||||
TAILQ_REMOVE(&lcparent->cells, lc, entry);
|
TAILQ_REMOVE(&lcparent->cells, lc, entry);
|
||||||
layout_free_cell(lc);
|
layout_free_cell(lc, 0);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,7 +741,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
|
|||||||
|
|
||||||
/* Remove this from the parent's list. */
|
/* Remove this from the parent's list. */
|
||||||
TAILQ_REMOVE(&lcparent->cells, lc, entry);
|
TAILQ_REMOVE(&lcparent->cells, lc, entry);
|
||||||
layout_free_cell(lc);
|
layout_free_cell(lc, 0);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
/*
|
/*
|
||||||
@@ -694,6 +753,7 @@ out:
|
|||||||
TAILQ_REMOVE(&lcparent->cells, lc, entry);
|
TAILQ_REMOVE(&lcparent->cells, lc, entry);
|
||||||
|
|
||||||
lc->parent = lcparent->parent;
|
lc->parent = lcparent->parent;
|
||||||
|
|
||||||
if (lc->parent == NULL) {
|
if (lc->parent == NULL) {
|
||||||
if (layout_cell_is_tiled(lc)) {
|
if (layout_cell_is_tiled(lc)) {
|
||||||
lc->xoff = 0;
|
lc->xoff = 0;
|
||||||
@@ -703,7 +763,7 @@ out:
|
|||||||
} else
|
} else
|
||||||
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
|
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
|
||||||
|
|
||||||
layout_free_cell(lcparent);
|
layout_free_cell(lcparent, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,9 +781,9 @@ layout_init(struct window *w, struct window_pane *wp)
|
|||||||
|
|
||||||
/* Free layout for pane. */
|
/* Free layout for pane. */
|
||||||
void
|
void
|
||||||
layout_free(struct window *w)
|
layout_free(struct window *w, int only_nodes)
|
||||||
{
|
{
|
||||||
layout_free_cell(w->layout_root);
|
layout_free_cell(w->layout_root, only_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resize the entire layout after window resize. */
|
/* Resize the entire layout after window resize. */
|
||||||
@@ -746,7 +806,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
|
|||||||
* out proportionately - this should leave the layout fitting the new
|
* out proportionately - this should leave the layout fitting the new
|
||||||
* window size.
|
* window size.
|
||||||
*/
|
*/
|
||||||
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING))
|
if (!layout_cell_is_tiled(lc))
|
||||||
return;
|
return;
|
||||||
xchange = sx - lc->sx;
|
xchange = sx - lc->sx;
|
||||||
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
|
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
|
||||||
@@ -814,7 +874,7 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Resize a floating pane to an absolute size. */
|
/* Resize a floating pane to an absolute size. */
|
||||||
void
|
int
|
||||||
layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
|
layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
|
||||||
u_int size, char **cause)
|
u_int size, char **cause)
|
||||||
{
|
{
|
||||||
@@ -822,7 +882,7 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
|
|||||||
|
|
||||||
if (~lc->flags & LAYOUT_CELL_FLOATING) {
|
if (~lc->flags & LAYOUT_CELL_FLOATING) {
|
||||||
*cause = xstrdup("pane is not floating");
|
*cause = xstrdup("pane is not floating");
|
||||||
return;
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window_pane_get_pane_lines(wp) != PANE_LINES_NONE &&
|
if (window_pane_get_pane_lines(wp) != PANE_LINES_NONE &&
|
||||||
@@ -830,23 +890,24 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
|
|||||||
size -= 2;
|
size -= 2;
|
||||||
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
|
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
|
||||||
*cause = xstrdup("size is too big or too small");
|
*cause = xstrdup("size is too big or too small");
|
||||||
return;
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == LAYOUT_TOPBOTTOM) {
|
if (type == LAYOUT_TOPBOTTOM) {
|
||||||
if (lc->sy == size)
|
if (lc->sy == size)
|
||||||
return;
|
return (0);
|
||||||
lc->sy = size;
|
lc->sy = size;
|
||||||
} else {
|
} else {
|
||||||
if (lc->sx == size)
|
if (lc->sx == size)
|
||||||
return;
|
return (0);
|
||||||
lc->sx = size;
|
lc->sx = size;
|
||||||
}
|
}
|
||||||
redraw_invalidate_scene(wp->window);
|
redraw_invalidate_scene(wp->window);
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resize a floating pane relative to its current size. */
|
/* Resize a floating pane relative to its current size. */
|
||||||
void
|
int
|
||||||
layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
|
layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
|
||||||
int change, int opposite, char **cause)
|
int change, int opposite, char **cause)
|
||||||
{
|
{
|
||||||
@@ -855,16 +916,16 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
|
|||||||
|
|
||||||
if (~lc->flags & LAYOUT_CELL_FLOATING) {
|
if (~lc->flags & LAYOUT_CELL_FLOATING) {
|
||||||
*cause = xstrdup("pane is not floating");
|
*cause = xstrdup("pane is not floating");
|
||||||
return;
|
return (-1);
|
||||||
}
|
}
|
||||||
if (change == 0)
|
if (change == 0)
|
||||||
return;
|
return (0);
|
||||||
|
|
||||||
if (type == LAYOUT_TOPBOTTOM) {
|
if (type == LAYOUT_TOPBOTTOM) {
|
||||||
size = lc->sy + change;
|
size = lc->sy + change;
|
||||||
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
|
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
|
||||||
*cause = xstrdup("change is too big or too small");
|
*cause = xstrdup("change is too big or too small");
|
||||||
return;
|
return (-1);
|
||||||
}
|
}
|
||||||
lc->sy = size;
|
lc->sy = size;
|
||||||
if (opposite)
|
if (opposite)
|
||||||
@@ -873,13 +934,14 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
|
|||||||
size = lc->sx + change;
|
size = lc->sx + change;
|
||||||
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
|
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
|
||||||
*cause = xstrdup("change is too big or too small");
|
*cause = xstrdup("change is too big or too small");
|
||||||
return;
|
return (-1);
|
||||||
}
|
}
|
||||||
lc->sx = size;
|
lc->sx = size;
|
||||||
if (opposite)
|
if (opposite)
|
||||||
lc->xoff -= change;
|
lc->xoff -= change;
|
||||||
}
|
}
|
||||||
redraw_invalidate_scene(wp->window);
|
redraw_invalidate_scene(wp->window);
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resize a layout cell. */
|
/* Resize a layout cell. */
|
||||||
@@ -1176,6 +1238,100 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replaces the provided layout cell with a new node of the specified type and
|
||||||
|
* inserts the cell into it. Used when creating new cells requires a different
|
||||||
|
* layout type, or when the root layout is a window pane.
|
||||||
|
*/
|
||||||
|
struct layout_cell *
|
||||||
|
layout_replace_with_node(struct window *w, struct layout_cell *lc,
|
||||||
|
enum layout_type type)
|
||||||
|
{
|
||||||
|
struct layout_cell *lcparent;
|
||||||
|
|
||||||
|
lcparent = layout_create_cell(lc->parent);
|
||||||
|
layout_make_node(lcparent, type);
|
||||||
|
layout_set_size(lcparent, lc->sx, lc->sy, lc->xoff, lc->yoff);
|
||||||
|
if (lc->parent == NULL)
|
||||||
|
w->layout_root = lcparent;
|
||||||
|
else
|
||||||
|
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
|
||||||
|
|
||||||
|
/* Insert the old cell. */
|
||||||
|
lc->parent = lcparent;
|
||||||
|
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
|
||||||
|
|
||||||
|
return (lcparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checks if there is enough space for two new panes. */
|
||||||
|
int
|
||||||
|
layout_split_check_space(struct window_pane *wp, struct layout_cell *lc,
|
||||||
|
enum layout_type type)
|
||||||
|
{
|
||||||
|
struct style *sb_style = &wp->scrollbar_style;
|
||||||
|
u_int minimum, sx = lc->sx, sy = lc->sy;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (lc->flags & LAYOUT_CELL_FLOATING)
|
||||||
|
fatalx("floating cells cannot be split");
|
||||||
|
|
||||||
|
status = window_get_pane_status(wp->window);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case LAYOUT_LEFTRIGHT:
|
||||||
|
if (wp->window->sb == PANE_SCROLLBARS_ALWAYS) {
|
||||||
|
minimum = PANE_MINIMUM * 2 + sb_style->width +
|
||||||
|
sb_style->pad;
|
||||||
|
} else
|
||||||
|
minimum = PANE_MINIMUM * 2 + 1;
|
||||||
|
if (sx < minimum)
|
||||||
|
return (0);
|
||||||
|
break;
|
||||||
|
case LAYOUT_TOPBOTTOM:
|
||||||
|
if (layout_add_horizontal_border(wp->window, lc, status))
|
||||||
|
minimum = PANE_MINIMUM * 2 + 2;
|
||||||
|
else
|
||||||
|
minimum = PANE_MINIMUM * 2 + 1;
|
||||||
|
if (sy < minimum)
|
||||||
|
return (0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fatalx("bad layout type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculates the new cell sizes when splitting a pane. */
|
||||||
|
void
|
||||||
|
layout_split_sizes(struct layout_cell *lc, int size, int before,
|
||||||
|
enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size)
|
||||||
|
{
|
||||||
|
u_int s1, s2, ss;
|
||||||
|
u_int sx = lc->sx, sy = lc->sy;
|
||||||
|
|
||||||
|
if (type == LAYOUT_LEFTRIGHT)
|
||||||
|
ss = sx;
|
||||||
|
else
|
||||||
|
ss = sy;
|
||||||
|
if (size < 0)
|
||||||
|
s2 = ((ss + 1) / 2) - 1;
|
||||||
|
else if (before)
|
||||||
|
s2 = ss - size - 1;
|
||||||
|
else
|
||||||
|
s2 = size;
|
||||||
|
if (s2 < PANE_MINIMUM)
|
||||||
|
s2 = PANE_MINIMUM;
|
||||||
|
else if (s2 > sx - 2)
|
||||||
|
s2 = ss - 2;
|
||||||
|
s1 = ss - 1 - s2;
|
||||||
|
|
||||||
|
*size1 = s1;
|
||||||
|
*size2 = s2;
|
||||||
|
*saved_size = ss;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split a pane into two. size is a hint, or -1 for default half/half
|
* Split a pane into two. size is a hint, or -1 for default half/half
|
||||||
* split. This must be followed by layout_assign_pane before much else happens!
|
* split. This must be followed by layout_assign_pane before much else happens!
|
||||||
@@ -1185,11 +1341,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
|||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
|
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
|
||||||
struct style *sb_style = &wp->scrollbar_style;
|
u_int sx, sy, xoff, yoff, size1, size2;
|
||||||
u_int sx, sy, xoff, yoff, size1, size2, minimum;
|
|
||||||
u_int new_size, saved_size, resize_first = 0;
|
u_int new_size, saved_size, resize_first = 0;
|
||||||
int full_size = (flags & SPAWN_FULLSIZE), status;
|
int full_size = (flags & SPAWN_FULLSIZE);
|
||||||
int scrollbars;
|
int before = (flags & SPAWN_BEFORE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If full_size is specified, add a new cell at the top of the window
|
* If full_size is specified, add a new cell at the top of the window
|
||||||
@@ -1199,8 +1354,6 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
|||||||
lc = wp->window->layout_root;
|
lc = wp->window->layout_root;
|
||||||
else
|
else
|
||||||
lc = wp->layout_cell;
|
lc = wp->layout_cell;
|
||||||
status = window_get_pane_status(wp->window);
|
|
||||||
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
|
|
||||||
|
|
||||||
/* Copy the old cell size. */
|
/* Copy the old cell size. */
|
||||||
sx = lc->sx;
|
sx = lc->sx;
|
||||||
@@ -1209,47 +1362,14 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
|||||||
yoff = lc->yoff;
|
yoff = lc->yoff;
|
||||||
|
|
||||||
/* Check there is enough space for the two new panes. */
|
/* Check there is enough space for the two new panes. */
|
||||||
switch (type) {
|
if (!layout_split_check_space(wp, lc, type))
|
||||||
case LAYOUT_LEFTRIGHT:
|
return (NULL);
|
||||||
if (scrollbars) {
|
|
||||||
minimum = PANE_MINIMUM * 2 + sb_style->width +
|
|
||||||
sb_style->pad;
|
|
||||||
} else
|
|
||||||
minimum = PANE_MINIMUM * 2 + 1;
|
|
||||||
if (sx < minimum)
|
|
||||||
return (NULL);
|
|
||||||
break;
|
|
||||||
case LAYOUT_TOPBOTTOM:
|
|
||||||
if (layout_add_horizontal_border(wp->window, lc, status))
|
|
||||||
minimum = PANE_MINIMUM * 2 + 2;
|
|
||||||
else
|
|
||||||
minimum = PANE_MINIMUM * 2 + 1;
|
|
||||||
if (sy < minimum)
|
|
||||||
return (NULL);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fatalx("bad layout type");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate new cell sizes. size is the target size or -1 for middle
|
* Calculate new cell sizes. size is the target size or -1 for middle
|
||||||
* split, size1 is the size of the top/left and size2 the bottom/right.
|
* split, size1 is the size of the top/left and size2 the bottom/right.
|
||||||
*/
|
*/
|
||||||
if (type == LAYOUT_LEFTRIGHT)
|
layout_split_sizes(lc, size, before, type, &size1, &size2, &saved_size);
|
||||||
saved_size = sx;
|
|
||||||
else
|
|
||||||
saved_size = sy;
|
|
||||||
if (size < 0)
|
|
||||||
size2 = ((saved_size + 1) / 2) - 1;
|
|
||||||
else if (flags & SPAWN_BEFORE)
|
|
||||||
size2 = saved_size - size - 1;
|
|
||||||
else
|
|
||||||
size2 = size;
|
|
||||||
if (size2 < PANE_MINIMUM)
|
|
||||||
size2 = PANE_MINIMUM;
|
|
||||||
else if (size2 > saved_size - 2)
|
|
||||||
size2 = saved_size - 2;
|
|
||||||
size1 = saved_size - 1 - size2;
|
|
||||||
|
|
||||||
/* Which size are we using? */
|
/* Which size are we using? */
|
||||||
if (flags & SPAWN_BEFORE)
|
if (flags & SPAWN_BEFORE)
|
||||||
@@ -1307,17 +1427,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Create and insert the replacement parent. */
|
/* Create and insert the replacement parent. */
|
||||||
lcparent = layout_create_cell(lc->parent);
|
lcparent = layout_replace_with_node(wp->window, lc, type);
|
||||||
layout_make_node(lcparent, type);
|
|
||||||
layout_set_size(lcparent, sx, sy, xoff, yoff);
|
|
||||||
if (lc->parent == NULL)
|
|
||||||
wp->window->layout_root = lcparent;
|
|
||||||
else
|
|
||||||
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
|
|
||||||
|
|
||||||
/* Insert the old cell. */
|
|
||||||
lc->parent = lcparent;
|
|
||||||
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
|
|
||||||
|
|
||||||
/* Create the new child cell. */
|
/* Create the new child cell. */
|
||||||
lcnew = layout_create_cell(lcparent);
|
lcnew = layout_create_cell(lcparent);
|
||||||
@@ -1376,14 +1486,7 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
|
|||||||
* Adding a pane to a root that isn't node. Must create and
|
* Adding a pane to a root that isn't node. Must create and
|
||||||
* insert a new root.
|
* insert a new root.
|
||||||
*/
|
*/
|
||||||
lcparent = layout_create_cell(NULL);
|
lcparent = layout_replace_with_node(w, lc, LAYOUT_TOPBOTTOM);
|
||||||
layout_make_node(lcparent, LAYOUT_TOPBOTTOM);
|
|
||||||
layout_set_size(lcparent, w->sx, w->sy, 0, 0);
|
|
||||||
w->layout_root = lcparent;
|
|
||||||
|
|
||||||
/* Insert the old cell. */
|
|
||||||
lc->parent = lcparent;
|
|
||||||
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lcnew = layout_create_cell(lcparent);
|
lcnew = layout_create_cell(lcparent);
|
||||||
@@ -1425,7 +1528,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
|
|||||||
|
|
||||||
number = 0;
|
number = 0;
|
||||||
TAILQ_FOREACH (lc, &parent->cells, entry)
|
TAILQ_FOREACH (lc, &parent->cells, entry)
|
||||||
number++;
|
if (layout_cell_is_tiled(lc))
|
||||||
|
number++;
|
||||||
if (number <= 1)
|
if (number <= 1)
|
||||||
return (0);
|
return (0);
|
||||||
status = window_get_pane_status(w);
|
status = window_get_pane_status(w);
|
||||||
@@ -1453,6 +1557,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
|
|||||||
|
|
||||||
changed = 0;
|
changed = 0;
|
||||||
TAILQ_FOREACH (lc, &parent->cells, entry) {
|
TAILQ_FOREACH (lc, &parent->cells, entry) {
|
||||||
|
if (!layout_cell_is_tiled(lc))
|
||||||
|
continue;
|
||||||
change = 0;
|
change = 0;
|
||||||
if (parent->type == LAYOUT_LEFTRIGHT) {
|
if (parent->type == LAYOUT_LEFTRIGHT) {
|
||||||
change = each - (int)lc->sx;
|
change = each - (int)lc->sx;
|
||||||
@@ -1561,16 +1667,35 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
|
|||||||
return (lc);
|
return (lc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get a new floating cell. */
|
|
||||||
struct layout_cell *
|
struct layout_cell *
|
||||||
layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
||||||
enum pane_lines lines, struct window *w, struct window_pane *wp,
|
enum pane_lines lines, struct window *w, struct window_pane *wp,
|
||||||
char **cause)
|
char **cause)
|
||||||
{
|
{
|
||||||
struct layout_cell *lcnew;
|
struct layout_cell *lcnew;
|
||||||
int sx = w->sx / 2, sy = w->sy / 4;
|
u_int sx = UINT_MAX, sy = UINT_MAX;
|
||||||
int ox = INT_MAX, oy = INT_MAX;
|
int ox = INT_MAX, oy = INT_MAX;
|
||||||
char *error;
|
|
||||||
|
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
|
||||||
|
cause) != 0)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy);
|
||||||
|
return (lcnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
||||||
|
enum pane_lines lines, struct window *w, u_int *sxp, u_int *syp, int *oxp,
|
||||||
|
int *oyp, char **cause)
|
||||||
|
{
|
||||||
|
int sx, sy, ox, oy;
|
||||||
|
char *error = NULL;
|
||||||
|
|
||||||
|
sx = *sxp == UINT_MAX ? w->sx / 2 : *sxp;
|
||||||
|
sy = *syp == UINT_MAX ? w->sy / 4 : *syp;
|
||||||
|
ox = *oxp == INT_MAX ? INT_MAX : *oxp;
|
||||||
|
oy = *oyp == INT_MAX ? INT_MAX : *oyp;
|
||||||
|
|
||||||
if (args_has(args, 'x')) {
|
if (args_has(args, 'x')) {
|
||||||
sx = args_percentage_and_expand(args, 'x', 0, PANE_MAXIMUM,
|
sx = args_percentage_and_expand(args, 'x', 0, PANE_MAXIMUM,
|
||||||
@@ -1578,7 +1703,7 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
|||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
xasprintf(cause, "position %s", error);
|
xasprintf(cause, "position %s", error);
|
||||||
free(error);
|
free(error);
|
||||||
return (NULL);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (lines != PANE_LINES_NONE)
|
if (lines != PANE_LINES_NONE)
|
||||||
sx -= 2;
|
sx -= 2;
|
||||||
@@ -1589,7 +1714,7 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
|||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
xasprintf(cause, "position %s", error);
|
xasprintf(cause, "position %s", error);
|
||||||
free(error);
|
free(error);
|
||||||
return (NULL);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (lines != PANE_LINES_NONE)
|
if (lines != PANE_LINES_NONE)
|
||||||
sy -= 2;
|
sy -= 2;
|
||||||
@@ -1598,18 +1723,18 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
|||||||
ox = args_percentage_and_expand(args, 'X', -sx, w->sx,
|
ox = args_percentage_and_expand(args, 'X', -sx, w->sx,
|
||||||
w->sx, item, &error);
|
w->sx, item, &error);
|
||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
xasprintf(cause, "size %s", error);
|
xasprintf(cause, "position %s", error);
|
||||||
free(error);
|
free(error);
|
||||||
return (NULL);
|
return (-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (args_has(args, 'Y')) {
|
if (args_has(args, 'Y')) {
|
||||||
oy = args_percentage_and_expand(args, 'Y', -sy, w->sy,
|
oy = args_percentage_and_expand(args, 'Y', -sy, w->sy,
|
||||||
w->sy, item, &error);
|
w->sy, item, &error);
|
||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
xasprintf(cause, "size %s", error);
|
xasprintf(cause, "position %s", error);
|
||||||
free(error);
|
free(error);
|
||||||
return (NULL);
|
return (-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1640,15 +1765,18 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
|||||||
|
|
||||||
if (sx < PANE_MINIMUM || sx > PANE_MAXIMUM) {
|
if (sx < PANE_MINIMUM || sx > PANE_MAXIMUM) {
|
||||||
*cause = xstrdup("invalid width");
|
*cause = xstrdup("invalid width");
|
||||||
return (NULL);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (sy < PANE_MINIMUM || sy > PANE_MAXIMUM) {
|
if (sy < PANE_MINIMUM || sy > PANE_MAXIMUM) {
|
||||||
*cause = xstrdup("invalid height");
|
*cause = xstrdup("invalid height");
|
||||||
return (NULL);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy);
|
*sxp = sx;
|
||||||
return (lcnew);
|
*syp = sy;
|
||||||
|
*oxp = ox;
|
||||||
|
*oyp = oy;
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1682,7 +1810,75 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
|
|||||||
layout_resize_adjust(w, lcneighbour, type, change);
|
layout_resize_adjust(w, lcneighbour, type, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zeroing out the cell geometry until the cell is retiled. */
|
/*
|
||||||
layout_set_size(lc, 0, 0, 0, 0);
|
* Zeroing out the cell geometry until the cell is retiled unless this
|
||||||
|
* is the top level node.
|
||||||
|
*/
|
||||||
|
if (lc->parent != NULL)
|
||||||
|
layout_set_size(lc, 0, 0, 0, 0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inserts a cell back into the tiled layout by taking half the space from its
|
||||||
|
* nearest neighbour.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
layout_insert_tile(struct window *w, struct layout_cell *lc)
|
||||||
|
{
|
||||||
|
struct layout_cell *lcneighbour, *lctiled, *lcparent;
|
||||||
|
enum layout_type type;
|
||||||
|
u_int size1, size2, saved_size;
|
||||||
|
|
||||||
|
if (lc == NULL)
|
||||||
|
fatalx("layout cell cannot be null when tiling");
|
||||||
|
|
||||||
|
lcparent = lc->parent;
|
||||||
|
if (lc->flags & LAYOUT_CELL_FLOATING)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
if (lcparent == NULL) {
|
||||||
|
/* Only pane in the layout. */
|
||||||
|
layout_set_size(lc, w->sx, w->sy, 0, 0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
type = lcparent->type;
|
||||||
|
lcneighbour = layout_cell_get_neighbour(lc);
|
||||||
|
if (lcneighbour == NULL) {
|
||||||
|
/*
|
||||||
|
* This will become the only visible cell in the parent.
|
||||||
|
* Tile the parent, then set the child's 'split' size.
|
||||||
|
*/
|
||||||
|
layout_insert_tile(w, lcparent);
|
||||||
|
if (type == LAYOUT_LEFTRIGHT)
|
||||||
|
size1 = lcparent->sx;
|
||||||
|
else
|
||||||
|
size1 = lcparent->sy;
|
||||||
|
layout_resize_set_size(w, lc, type, size1);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If the neighbour is a node, a tiled child in the subtree of
|
||||||
|
* the neighbour is needed to check for space.
|
||||||
|
*/
|
||||||
|
lctiled = layout_cell_get_first_tiled(lcneighbour);
|
||||||
|
if (!layout_split_check_space(lctiled->wp, lcneighbour, type))
|
||||||
|
return (0);
|
||||||
|
layout_split_sizes(lcneighbour, -1, 0, type, &size1, &size2,
|
||||||
|
&saved_size);
|
||||||
|
layout_resize_set_size(w, lc, type, size1);
|
||||||
|
layout_resize_set_size(w, lcneighbour, type, size2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setting opposite of the 'split' size to that of the parent. */
|
||||||
|
if (lcparent->type == LAYOUT_LEFTRIGHT) {
|
||||||
|
size1 = lcparent->sy;
|
||||||
|
type = LAYOUT_TOPBOTTOM;
|
||||||
|
} else {
|
||||||
|
size1 = lcparent->sx;
|
||||||
|
type = LAYOUT_LEFTRIGHT;
|
||||||
|
}
|
||||||
|
layout_resize_set_size(w, lc, type, size1);
|
||||||
|
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|||||||
271
mode-tree.c
271
mode-tree.c
@@ -36,6 +36,24 @@ enum mode_tree_preview {
|
|||||||
MODE_TREE_PREVIEW_BIG
|
MODE_TREE_PREVIEW_BIG
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MODE_TREE_PREFIX_STYLE \
|
||||||
|
"#{?mode_tree_selected,#[default]#[noacs]," \
|
||||||
|
"#[fg=themelightgrey]#[bg=default]#[noacs]}"
|
||||||
|
|
||||||
|
#define MODE_TREE_PREFIX_FORMAT \
|
||||||
|
MODE_TREE_PREFIX_STYLE \
|
||||||
|
"#{p/#{mode_tree_key_width}:" \
|
||||||
|
"#{?#{!=:#{mode_tree_key},},(#{mode_tree_key}),}}" \
|
||||||
|
"#{R:#{?mode_tree_parent_last, ," \
|
||||||
|
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
||||||
|
"#{mode_tree_repeat}}" \
|
||||||
|
"#{?mode_tree_branch," \
|
||||||
|
"#[acs]#{?mode_tree_last,mq,tq}" MODE_TREE_PREFIX_STYLE "> ,}" \
|
||||||
|
"#{?mode_tree_has_children," \
|
||||||
|
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
||||||
|
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
||||||
|
"#{?mode_tree_flat,, }}"
|
||||||
|
|
||||||
struct mode_tree_item;
|
struct mode_tree_item;
|
||||||
struct mode_tree_prompt;
|
struct mode_tree_prompt;
|
||||||
TAILQ_HEAD(mode_tree_list, mode_tree_item);
|
TAILQ_HEAD(mode_tree_list, mode_tree_item);
|
||||||
@@ -159,31 +177,51 @@ static const struct menu_item mode_tree_menu_items[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const char* mode_tree_help_start[] = {
|
static const char* mode_tree_help_start[] = {
|
||||||
"#[bold] Up, k #[default]#[acs]x#[default] Move cursor up",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] Down, j #[default]#[acs]x#[default] Move cursor down",
|
" Up, k #[#{E:tree-mode-border-style},acs]x#[default] Move cursor up",
|
||||||
"#[bold] g #[default]#[acs]x#[default] Go to top",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] G #[default]#[acs]x#[default] Go to bottom",
|
" Down, j #[#{E:tree-mode-border-style},acs]x#[default] Move cursor down",
|
||||||
"#[bold] PPage, C-b #[default]#[acs]x#[default] Page up",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] NPage, C-f #[default]#[acs]x#[default] Page down",
|
" g #[#{E:tree-mode-border-style},acs]x#[default] Go to top",
|
||||||
"#[bold] Left, h #[default]#[acs]x#[default] Collapse %1",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] Right, l #[default]#[acs]x#[default] Expand %1",
|
" G #[#{E:tree-mode-border-style},acs]x#[default] Go to bottom",
|
||||||
"#[bold] M-- #[default]#[acs]x#[default] Collapse all %1s",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] M-+ #[default]#[acs]x#[default] Expand all %1s",
|
" PPage, C-b #[#{E:tree-mode-border-style},acs]x#[default] Page up",
|
||||||
"#[bold] t #[default]#[acs]x#[default] Toggle %1 tag",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] T #[default]#[acs]x#[default] Untag all %1s",
|
" NPage, C-f #[#{E:tree-mode-border-style},acs]x#[default] Page down",
|
||||||
"#[bold] C-t #[default]#[acs]x#[default] Tag all %1s",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] C-s #[default]#[acs]x#[default] Search forward",
|
" Left, h #[#{E:tree-mode-border-style},acs]x#[default] Collapse %1",
|
||||||
"#[bold] C-r #[default]#[acs]x#[default] Search backward",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] n #[default]#[acs]x#[default] Repeat search forward",
|
" Right, l #[#{E:tree-mode-border-style},acs]x#[default] Expand %1",
|
||||||
"#[bold] N #[default]#[acs]x#[default] Repeat search backward",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] f #[default]#[acs]x#[default] Filter %1s",
|
" M-- #[#{E:tree-mode-border-style},acs]x#[default] Collapse all %1s",
|
||||||
"#[bold] O #[default]#[acs]x#[default] Change sort order",
|
"#[fg=themelightgrey]"
|
||||||
"#[bold] r #[default]#[acs]x#[default] Reverse sort order",
|
" M-+ #[#{E:tree-mode-border-style},acs]x#[default] Expand all %1s",
|
||||||
"#[bold] v #[default]#[acs]x#[default] Toggle preview",
|
"#[fg=themelightgrey]"
|
||||||
|
" t #[#{E:tree-mode-border-style},acs]x#[default] Toggle %1 tag",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" T #[#{E:tree-mode-border-style},acs]x#[default] Untag all %1s",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" C-t #[#{E:tree-mode-border-style},acs]x#[default] Tag all %1s",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" C-s #[#{E:tree-mode-border-style},acs]x#[default] Search forward",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" n #[#{E:tree-mode-border-style},acs]x#[default] Repeat search forward",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" N #[#{E:tree-mode-border-style},acs]x#[default] Repeat search backward",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" f #[#{E:tree-mode-border-style},acs]x#[default] Filter %1s",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" O #[#{E:tree-mode-border-style},acs]x#[default] Change sort order",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" r #[#{E:tree-mode-border-style},acs]x#[default] Reverse sort order",
|
||||||
|
"#[fg=themelightgrey]"
|
||||||
|
" v #[#{E:tree-mode-border-style},acs]x#[default] Toggle preview",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
static const char* mode_tree_help_end[] = {
|
static const char* mode_tree_help_end[] = {
|
||||||
"#[bold] q, Escape #[default]#[acs]x#[default] Exit mode",
|
"#[fg=themelightgrey]"
|
||||||
|
" q, Escape #[#{E:tree-mode-border-style},acs]x#[default] Exit mode",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
#define MODE_TREE_HELP_DEFAULT_WIDTH 39
|
#define MODE_TREE_HELP_DEFAULT_WIDTH 39
|
||||||
@@ -754,8 +792,8 @@ mode_tree_no_tag(struct mode_tree_item *mti)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the alignment of mti->name: -1 to align left, 0 (default) to not align,
|
* Set the alignment of the item name: -1 to align left, 0 (default) to not
|
||||||
* or 1 to align right.
|
* align, or 1 to align right.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
mode_tree_align(struct mode_tree_item *mti, int align)
|
mode_tree_align(struct mode_tree_item *mti, int align)
|
||||||
@@ -784,25 +822,34 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
struct mode_tree_item *mti;
|
struct mode_tree_item *mti;
|
||||||
struct options *oo = wp->window->options;
|
struct options *oo = wp->window->options;
|
||||||
struct screen_write_ctx ctx;
|
struct screen_write_ctx ctx;
|
||||||
struct grid_cell gc0, gc;
|
struct format_tree *ft;
|
||||||
u_int w, h, i, j, sy, box_x, box_y, width;
|
struct grid_cell gc0, gc, box_gc;
|
||||||
char *text, *start, *key;
|
u_int w, h, i, sy, box_x, box_y;
|
||||||
const char *tag, *symbol;
|
u_int width, text_width, prefix_width, left;
|
||||||
size_t size, n;
|
char *text, *prefix;
|
||||||
int keylen, pad, alignlen[mtd->maxdepth + 1];
|
const char *tag, *separator;
|
||||||
|
size_t n;
|
||||||
|
int keylen, alignlen[mtd->maxdepth + 1];
|
||||||
|
int dfg, dfg0;
|
||||||
|
|
||||||
if (mtd->line_size == 0)
|
if (mtd->line_size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memcpy(&gc0, &grid_default_cell, sizeof gc0);
|
memcpy(&gc0, &grid_default_cell, sizeof gc0);
|
||||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||||
style_apply(&gc, oo, "mode-style", NULL);
|
style_apply(&gc, oo, "tree-mode-selection-style", NULL);
|
||||||
|
memcpy(&box_gc, &grid_default_cell, sizeof box_gc);
|
||||||
|
style_apply(&box_gc, oo, "tree-mode-border-style", NULL);
|
||||||
|
|
||||||
|
dfg = gc.fg;
|
||||||
|
dfg0 = gc0.fg;
|
||||||
|
|
||||||
w = mtd->width;
|
w = mtd->width;
|
||||||
h = mtd->height;
|
h = mtd->height;
|
||||||
|
|
||||||
screen_write_start(&ctx, s);
|
screen_write_start(&ctx, s);
|
||||||
screen_write_clearscreen(&ctx, 8);
|
screen_write_clearscreen(&ctx, 8);
|
||||||
|
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
|
||||||
|
|
||||||
keylen = 0;
|
keylen = 0;
|
||||||
for (i = 0; i < mtd->line_size; i++) {
|
for (i = 0; i < mtd->line_size; i++) {
|
||||||
@@ -833,81 +880,98 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
|
|
||||||
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
|
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
|
||||||
|
|
||||||
pad = keylen - 2 - mti->keylen;
|
|
||||||
if (mti->key != KEYC_NONE)
|
if (mti->key != KEYC_NONE)
|
||||||
xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
|
format_add(ft, "mode_tree_key", "%s", mti->keystr);
|
||||||
else
|
else
|
||||||
key = xstrdup("");
|
format_add(ft, "mode_tree_key", "%s", "");
|
||||||
|
format_add(ft, "mode_tree_key_width", "%d", keylen);
|
||||||
if (line->flat)
|
format_add(ft, "mode_tree_selected", "%d", i == mtd->current);
|
||||||
symbol = "";
|
if (line->depth == 0) {
|
||||||
else if (TAILQ_EMPTY(&mti->children))
|
format_add(ft, "mode_tree_repeat", "%u", 0);
|
||||||
symbol = " ";
|
format_add(ft, "mode_tree_branch", "0");
|
||||||
else if (mti->expanded)
|
format_add(ft, "mode_tree_parent_last", "0");
|
||||||
symbol = "- ";
|
} else {
|
||||||
else
|
format_add(ft, "mode_tree_repeat", "%u",
|
||||||
symbol = "+ ";
|
line->depth - 1);
|
||||||
|
format_add(ft, "mode_tree_branch", "1");
|
||||||
if (line->depth == 0)
|
if (mti->parent != NULL &&
|
||||||
start = xstrdup(symbol);
|
mtd->line_list[mti->parent->line].last)
|
||||||
else {
|
format_add(ft, "mode_tree_parent_last", "1");
|
||||||
size = (4 * line->depth) + 32;
|
|
||||||
|
|
||||||
start = xcalloc(1, size);
|
|
||||||
for (j = 1; j < line->depth; j++) {
|
|
||||||
if (mti->parent != NULL &&
|
|
||||||
mtd->line_list[mti->parent->line].last)
|
|
||||||
strlcat(start, " ", size);
|
|
||||||
else
|
|
||||||
strlcat(start, "\001x\001 ", size);
|
|
||||||
}
|
|
||||||
if (line->last)
|
|
||||||
strlcat(start, "\001mq\001> ", size);
|
|
||||||
else
|
else
|
||||||
strlcat(start, "\001tq\001> ", size);
|
format_add(ft, "mode_tree_parent_last", "0");
|
||||||
strlcat(start, symbol, size);
|
|
||||||
}
|
}
|
||||||
|
if (TAILQ_EMPTY(&mti->children))
|
||||||
|
format_add(ft, "mode_tree_has_children", "0");
|
||||||
|
else
|
||||||
|
format_add(ft, "mode_tree_has_children", "1");
|
||||||
|
format_add(ft, "mode_tree_last", "%d", line->last);
|
||||||
|
format_add(ft, "mode_tree_expanded", "%d", mti->expanded);
|
||||||
|
format_add(ft, "mode_tree_flat", "%d", line->flat);
|
||||||
|
prefix = format_expand(ft, MODE_TREE_PREFIX_FORMAT);
|
||||||
|
prefix_width = format_width(prefix);
|
||||||
|
if (prefix_width > w)
|
||||||
|
prefix_width = w;
|
||||||
|
|
||||||
if (mti->tagged)
|
if (mti->tagged)
|
||||||
tag = "*";
|
tag = "*";
|
||||||
else
|
else
|
||||||
tag = "";
|
tag = "";
|
||||||
xasprintf(&text, "%-*s%s%*s%s%s", keylen, key, start,
|
if (mti->text != NULL)
|
||||||
mti->align * alignlen[line->depth], mti->name, tag,
|
separator = "#[fg=themelightgrey]: #[default]";
|
||||||
(mti->text != NULL) ? ": " : "" );
|
else
|
||||||
width = utf8_cstrwidth(text);
|
separator = "";
|
||||||
if (width > w)
|
xasprintf(&text, "%*s%s%s",
|
||||||
width = w;
|
mti->align * alignlen[line->depth], mti->name, tag, separator);
|
||||||
free(start);
|
text_width = format_width(text);
|
||||||
|
left = (prefix_width < w) ? (w - prefix_width) : 0;
|
||||||
|
if (text_width > left)
|
||||||
|
text_width = left;
|
||||||
|
width = prefix_width + text_width;
|
||||||
|
|
||||||
if (mti->tagged) {
|
if (mti->tagged) {
|
||||||
gc.attr ^= GRID_ATTR_BRIGHT;
|
gc.fg = COLOUR_THEME_CYAN|COLOUR_FLAG_THEME;
|
||||||
gc0.attr ^= GRID_ATTR_BRIGHT;
|
gc0.fg = COLOUR_THEME_CYAN|COLOUR_FLAG_THEME;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != mtd->current) {
|
if (i != mtd->current) {
|
||||||
screen_write_clearendofline(&ctx, 8);
|
screen_write_clearendofline(&ctx, 8);
|
||||||
screen_write_nputs(&ctx, w, &gc0, "%s", text);
|
format_draw(&ctx, &grid_default_cell, prefix_width,
|
||||||
if (mti->text != NULL) {
|
prefix, NULL, 0);
|
||||||
format_draw(&ctx, &gc0, w - width, mti->text,
|
if (left != 0) {
|
||||||
NULL, 0);
|
screen_write_cursormove(&ctx, prefix_width,
|
||||||
|
i - mtd->offset, 0);
|
||||||
|
format_draw(&ctx, &gc0, left, text, NULL, 0);
|
||||||
|
if (mti->text != NULL && width < w) {
|
||||||
|
screen_write_cursormove(&ctx, width,
|
||||||
|
i - mtd->offset, 0);
|
||||||
|
format_draw(&ctx, &gc0, w - width,
|
||||||
|
mti->text, NULL, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
screen_write_clearendofline(&ctx, gc.bg);
|
screen_write_clearendofline(&ctx, gc.bg);
|
||||||
screen_write_nputs(&ctx, w, &gc, "%s", text);
|
format_draw(&ctx, &gc, prefix_width, prefix, NULL, 0);
|
||||||
if (mti->text != NULL) {
|
if (left != 0) {
|
||||||
format_draw(&ctx, &gc, w - width, mti->text,
|
screen_write_cursormove(&ctx, prefix_width,
|
||||||
NULL, 0);
|
i - mtd->offset, 0);
|
||||||
|
format_draw(&ctx, &gc, left, text, NULL, 0);
|
||||||
|
if (mti->text != NULL && width < w) {
|
||||||
|
screen_write_cursormove(&ctx, width,
|
||||||
|
i - mtd->offset, 0);
|
||||||
|
format_draw(&ctx, &gc, w - width,
|
||||||
|
mti->text, NULL, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(text);
|
free(text);
|
||||||
free(key);
|
free(prefix);
|
||||||
|
|
||||||
if (mti->tagged) {
|
if (mti->tagged) {
|
||||||
gc.attr ^= GRID_ATTR_BRIGHT;
|
gc.fg = dfg;
|
||||||
gc0.attr ^= GRID_ATTR_BRIGHT;
|
gc0.fg = dfg0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
format_free(ft);
|
||||||
|
|
||||||
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
|
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
|
||||||
goto done;
|
goto done;
|
||||||
@@ -922,7 +986,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
mti = mti->parent;
|
mti = mti->parent;
|
||||||
|
|
||||||
screen_write_cursormove(&ctx, 0, h, 0);
|
screen_write_cursormove(&ctx, 0, h, 0);
|
||||||
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL);
|
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, &box_gc, NULL);
|
||||||
|
|
||||||
if (mtd->sort_crit.order_seq != NULL) {
|
if (mtd->sort_crit.order_seq != NULL) {
|
||||||
xasprintf(&text, " %s (sort: %s%s)%s%s%s", mti->name,
|
xasprintf(&text, " %s (sort: %s%s)%s%s%s", mti->name,
|
||||||
@@ -935,21 +999,21 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
xasprintf(&text, " %s", mti->name);
|
xasprintf(&text, " %s", mti->name);
|
||||||
if (w - 2 >= strlen(text)) {
|
if (w - 2 >= strlen(text)) {
|
||||||
screen_write_cursormove(&ctx, 1, h, 0);
|
screen_write_cursormove(&ctx, 1, h, 0);
|
||||||
screen_write_puts(&ctx, &gc0, "%s", text);
|
screen_write_puts(&ctx, &box_gc, "%s", text);
|
||||||
|
|
||||||
if (mtd->no_matches)
|
if (mtd->no_matches)
|
||||||
n = (sizeof "no matches") - 1;
|
n = (sizeof "no matches") - 1;
|
||||||
else
|
else
|
||||||
n = (sizeof "active") - 1;
|
n = (sizeof "active") - 1;
|
||||||
if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
|
if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
|
||||||
screen_write_puts(&ctx, &gc0, " (filter: ");
|
screen_write_puts(&ctx, &box_gc, " (filter: ");
|
||||||
if (mtd->no_matches)
|
if (mtd->no_matches)
|
||||||
screen_write_puts(&ctx, &gc, "no matches");
|
screen_write_puts(&ctx, &box_gc, "no matches");
|
||||||
else
|
else
|
||||||
screen_write_puts(&ctx, &gc0, "active");
|
screen_write_puts(&ctx, &box_gc, "active");
|
||||||
screen_write_puts(&ctx, &gc0, ") ");
|
screen_write_puts(&ctx, &box_gc, ") ");
|
||||||
} else
|
} else
|
||||||
screen_write_puts(&ctx, &gc0, " ");
|
screen_write_puts(&ctx, &box_gc, " ");
|
||||||
}
|
}
|
||||||
free(text);
|
free(text);
|
||||||
|
|
||||||
@@ -1078,6 +1142,7 @@ mode_tree_set_prompt(struct mode_tree_data *mtd, struct client *c,
|
|||||||
|
|
||||||
mtp = xcalloc(1, sizeof *mtp);
|
mtp = xcalloc(1, sizeof *mtp);
|
||||||
mtp->mtd = mtd;
|
mtp->mtd = mtd;
|
||||||
|
mtp->c = c;
|
||||||
mtp->inputcb = inputcb;
|
mtp->inputcb = inputcb;
|
||||||
mtp->freecb = freecb;
|
mtp->freecb = freecb;
|
||||||
mtp->data = data;
|
mtp->data = data;
|
||||||
@@ -1353,12 +1418,14 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
mode_tree_draw_help_line(struct screen_write_ctx *ctx,
|
mode_tree_draw_help_line(struct screen_write_ctx *ctx,
|
||||||
const struct grid_cell *gc, const char *line, const char *item, u_int x,
|
const struct grid_cell *gc, struct format_tree *ft, const char *line,
|
||||||
u_int y, u_int w)
|
const char *item, u_int x, u_int y, u_int w)
|
||||||
{
|
{
|
||||||
char *expanded;
|
char *expanded, *replaced;
|
||||||
|
|
||||||
expanded = cmd_template_replace(line, item, 1);
|
replaced = cmd_template_replace(line, item, 1);
|
||||||
|
expanded = format_expand(ft, replaced);
|
||||||
|
free(replaced);
|
||||||
screen_write_cursormove(ctx, x, y, 0);
|
screen_write_cursormove(ctx, x, y, 0);
|
||||||
screen_write_clearcharacter(ctx, w, gc->bg);
|
screen_write_clearcharacter(ctx, w, gc->bg);
|
||||||
screen_write_cursormove(ctx, x, y, 0);
|
screen_write_cursormove(ctx, x, y, 0);
|
||||||
@@ -1370,7 +1437,9 @@ static void
|
|||||||
mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
|
mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct screen *s = &mtd->screen;
|
struct screen *s = &mtd->screen;
|
||||||
struct grid_cell gc;
|
struct options *oo = mtd->wp->window->options;
|
||||||
|
struct grid_cell box_gc, gc;
|
||||||
|
struct format_tree *ft;
|
||||||
const char **line, **lines = NULL, *item = "item";
|
const char **line, **lines = NULL, *item = "item";
|
||||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||||
u_int x, y, w, h = 0, box_w, box_h;
|
u_int x, y, w, h = 0, box_w, box_h;
|
||||||
@@ -1396,18 +1465,22 @@ mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
|
|||||||
x = (sx - box_w) / 2;
|
x = (sx - box_w) / 2;
|
||||||
y = (sy - box_h) / 2;
|
y = (sy - box_h) / 2;
|
||||||
|
|
||||||
|
memcpy(&box_gc, &grid_default_cell, sizeof box_gc);
|
||||||
|
style_apply(&box_gc, oo, "tree-mode-border-style", NULL);
|
||||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||||
|
ft = format_create_defaults(NULL, NULL, NULL, NULL, mtd->wp);
|
||||||
screen_write_cursormove(ctx, x, y, 0);
|
screen_write_cursormove(ctx, x, y, 0);
|
||||||
screen_write_box(ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL);
|
screen_write_box(ctx, box_w, box_h, BOX_LINES_DEFAULT, &box_gc, NULL);
|
||||||
|
|
||||||
y++;
|
y++;
|
||||||
x++;
|
x++;
|
||||||
for (line = mode_tree_help_start; *line != NULL; line++, y++)
|
for (line = mode_tree_help_start; *line != NULL; line++, y++)
|
||||||
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w);
|
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
|
||||||
for (line = lines; line != NULL && *line != NULL; line++, y++)
|
for (line = lines; line != NULL && *line != NULL; line++, y++)
|
||||||
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w);
|
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
|
||||||
for (line = mode_tree_help_end; *line != NULL; line++, y++)
|
for (line = mode_tree_help_end; *line != NULL; line++, y++)
|
||||||
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w);
|
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
|
||||||
|
format_free(ft);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -1488,6 +1561,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
|||||||
*key = KEYC_NONE;
|
*key = KEYC_NONE;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
if (*key == KEYC_FOCUS_IN || *key == KEYC_FOCUS_OUT) {
|
||||||
|
*key = KEYC_NONE;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
mtd->help = 0;
|
mtd->help = 0;
|
||||||
mode_tree_draw(mtd);
|
mode_tree_draw(mtd);
|
||||||
*key = KEYC_NONE;
|
*key = KEYC_NONE;
|
||||||
|
|||||||
4
names.c
4
names.c
@@ -95,7 +95,7 @@ check_window_name(struct window *w)
|
|||||||
name = format_window_name(w);
|
name = format_window_name(w);
|
||||||
if (strcmp(name, w->name) != 0) {
|
if (strcmp(name, w->name) != 0) {
|
||||||
log_debug("@%u new name %s (was %s)", w->id, name, w->name);
|
log_debug("@%u new name %s (was %s)", w->id, name, w->name);
|
||||||
window_set_name(w, name, WINDOW_NAME_FORBID_EXT);
|
window_set_name(w, name, 1);
|
||||||
server_redraw_window_borders(w);
|
server_redraw_window_borders(w);
|
||||||
server_status_window(w);
|
server_status_window(w);
|
||||||
} else
|
} else
|
||||||
@@ -166,7 +166,7 @@ parse_window_name(const char *in)
|
|||||||
|
|
||||||
if (*name == '/')
|
if (*name == '/')
|
||||||
name = basename(name);
|
name = basename(name);
|
||||||
name = clean_name(name, WINDOW_NAME_FORBID);
|
name = clean_name(name, 0);
|
||||||
free(copy);
|
free(copy);
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return (xstrdup(""));
|
return (xstrdup(""));
|
||||||
|
|||||||
301
options-table.c
301
options-table.c
@@ -64,7 +64,7 @@ static const char *options_table_cursor_style_list[] = {
|
|||||||
"blinking-bar", "bar", NULL
|
"blinking-bar", "bar", NULL
|
||||||
};
|
};
|
||||||
static const char *options_table_pane_scrollbars_list[] = {
|
static const char *options_table_pane_scrollbars_list[] = {
|
||||||
"off", "modal", "on", NULL
|
"off", "modal", "on", "auto-hide", NULL
|
||||||
};
|
};
|
||||||
static const char *options_table_pane_scrollbars_position_list[] = {
|
static const char *options_table_pane_scrollbars_position_list[] = {
|
||||||
"right", "left", NULL
|
"right", "left", NULL
|
||||||
@@ -108,6 +108,9 @@ static const char *options_table_extended_keys_format_list[] = {
|
|||||||
static const char *options_table_allow_passthrough_list[] = {
|
static const char *options_table_allow_passthrough_list[] = {
|
||||||
"off", "on", "all", NULL
|
"off", "on", "all", NULL
|
||||||
};
|
};
|
||||||
|
static const char *options_table_theme_list[] = {
|
||||||
|
"detect", "terminal", "light", "dark", NULL
|
||||||
|
};
|
||||||
static const char *options_table_copy_mode_line_numbers_list[] = {
|
static const char *options_table_copy_mode_line_numbers_list[] = {
|
||||||
"off", "default", "absolute", "relative", "hybrid", NULL
|
"off", "default", "absolute", "relative", "hybrid", NULL
|
||||||
};
|
};
|
||||||
@@ -270,6 +273,7 @@ const struct options_name_map options_other_names[] = {
|
|||||||
{ "clock-mode-color", "clock-mode-colour" },
|
{ "clock-mode-color", "clock-mode-colour" },
|
||||||
{ "cursor-color", "cursor-colour" },
|
{ "cursor-color", "cursor-colour" },
|
||||||
{ "prompt-cursor-color", "prompt-cursor-colour" },
|
{ "prompt-cursor-color", "prompt-cursor-colour" },
|
||||||
|
{ "prompt-command-cursor-color", "prompt-command-cursor-colour" },
|
||||||
{ "pane-colors", "pane-colours" },
|
{ "pane-colors", "pane-colours" },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
@@ -445,7 +449,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.default_str = "default",
|
.default_str = "bg=themedarkgrey,fg=themewhite",
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Default style of menu."
|
.text = "Default style of menu."
|
||||||
},
|
},
|
||||||
@@ -454,7 +458,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.default_str = "bg=yellow,fg=black",
|
.default_str = "bg=themeyellow,fg=themeblack",
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Default style of selected menu item."
|
.text = "Default style of selected menu item."
|
||||||
},
|
},
|
||||||
@@ -462,7 +466,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "menu-border-style",
|
{ .name = "menu-border-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "default",
|
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Default style of menu borders."
|
.text = "Default style of menu borders."
|
||||||
@@ -537,6 +541,175 @@ const struct options_table_entry options_table[] = {
|
|||||||
"automatically detected."
|
"automatically detected."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "theme",
|
||||||
|
.type = OPTIONS_TABLE_CHOICE,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.choices = options_table_theme_list,
|
||||||
|
.default_num = 0,
|
||||||
|
.text = "Whether tmux should detect the terminal theme, use terminal "
|
||||||
|
"ANSI colours, or force the light or dark theme."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-black",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray5,black}",
|
||||||
|
.text = "Dark theme colour for black."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-white",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray90,white}",
|
||||||
|
.text = "Dark theme colour for white."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-light-grey",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray70,white}",
|
||||||
|
.text = "Dark theme colour for light grey."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-dark-grey",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray15,black}",
|
||||||
|
.text = "Dark theme colour for dark grey."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-green",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},yellowgreen,green}",
|
||||||
|
.text = "Dark theme colour for green."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-yellow",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},darkgoldenrod,yellow}",
|
||||||
|
.text = "Dark theme colour for yellow."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-red",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},indianred,red}",
|
||||||
|
.text = "Dark theme colour for red."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-blue",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},skyblue3,blue}",
|
||||||
|
.text = "Dark theme colour for blue."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-cyan",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},cadetblue,cyan}",
|
||||||
|
.text = "Dark theme colour for cyan."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "dark-theme-magenta",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},mediumpurple,magenta}",
|
||||||
|
.text = "Dark theme colour for magenta."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-black",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray10,black}",
|
||||||
|
.text = "Light theme colour for black."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-white",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray95,white}",
|
||||||
|
.text = "Light theme colour for white."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-light-grey",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray80,white}",
|
||||||
|
.text = "Light theme colour for light grey."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-dark-grey",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},gray45,black}",
|
||||||
|
.text = "Light theme colour for dark grey."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-green",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},seagreen,green}",
|
||||||
|
.text = "Light theme colour for green."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-yellow",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},darkgoldenrod,yellow}",
|
||||||
|
.text = "Light theme colour for yellow."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-red",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},indianred4,red}",
|
||||||
|
.text = "Light theme colour for red."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-blue",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},steelblue,blue}",
|
||||||
|
.text = "Light theme colour for blue."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-cyan",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},darkcyan,cyan}",
|
||||||
|
.text = "Light theme colour for cyan."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "light-theme-magenta",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "#{?#{e|>=:#{client_colours},256},purple4,magenta}",
|
||||||
|
.text = "Light theme colour for magenta."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "user-keys",
|
{ .name = "user-keys",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SERVER,
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
@@ -638,7 +811,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
.default_str = "red",
|
.default_str = "themered",
|
||||||
.text = "Colour of the active pane for 'display-panes'."
|
.text = "Colour of the active pane for 'display-panes'."
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -646,7 +819,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
.default_str = "blue",
|
.default_str = "themeblue",
|
||||||
.text = "Colour of not active panes for 'display-panes'."
|
.text = "Colour of not active panes for 'display-panes'."
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -737,7 +910,9 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "message-command-style",
|
{ .name = "message-command-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.default_str = "bg=black,fg=yellow,fill=black",
|
.default_str = "bg=themeblack,fg=themeyellow,"
|
||||||
|
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
|
||||||
|
"fill=themeblack}",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the command prompt when in command mode, if "
|
.text = "Style of the command prompt when in command mode, if "
|
||||||
@@ -766,9 +941,9 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "message-style",
|
{ .name = "message-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.default_str = "bg=yellow,fg=black,"
|
.default_str = "bg=themeyellow,fg=themeblack,"
|
||||||
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
|
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
|
||||||
"fill=yellow}",
|
"fill=themeyellow}",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of messages and the command prompt. "
|
.text = "Style of messages and the command prompt. "
|
||||||
@@ -967,7 +1142,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "status-style",
|
{ .name = "status-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.default_str = "bg=green,fg=black",
|
.default_str = "bg=themegreen,fg=themeblack",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the status line."
|
.text = "Style of the status line."
|
||||||
@@ -976,7 +1151,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "pane-status-current-style",
|
{ .name = "pane-status-current-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "default",
|
.default_str = "underscore",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the current pane in the status line."
|
.text = "Style of the current pane in the status line."
|
||||||
@@ -1000,6 +1175,15 @@ const struct options_table_entry options_table[] = {
|
|||||||
.text = "Colour of the cursor when in the command prompt."
|
.text = "Colour of the cursor when in the command prompt."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "prompt-command-cursor-colour",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
|
.default_str = "",
|
||||||
|
.text = "Colour of the cursor in the command prompt when in command "
|
||||||
|
"mode, if 'status-keys' is set to 'vi'."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "prompt-cursor-style",
|
{ .name = "prompt-cursor-style",
|
||||||
.type = OPTIONS_TABLE_CHOICE,
|
.type = OPTIONS_TABLE_CHOICE,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
@@ -1020,7 +1204,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "session-status-current-style",
|
{ .name = "session-status-current-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "default",
|
.default_str = "underscore",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the current session in the status line."
|
.text = "Style of the current session in the status line."
|
||||||
@@ -1151,7 +1335,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||||
.default_str = "blue",
|
.default_str = "themeblue",
|
||||||
.text = "Colour of the clock in clock mode."
|
.text = "Colour of the clock in clock mode."
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1166,7 +1350,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "copy-mode-match-style",
|
{ .name = "copy-mode-match-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "bg=cyan,fg=black",
|
.default_str = "bg=themecyan,fg=themeblack",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of search matches in copy mode."
|
.text = "Style of search matches in copy mode."
|
||||||
@@ -1175,7 +1359,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "copy-mode-current-match-style",
|
{ .name = "copy-mode-current-match-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "bg=magenta,fg=black",
|
.default_str = "bg=thememagenta,fg=themeblack",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the current search match in copy mode."
|
.text = "Style of the current search match in copy mode."
|
||||||
@@ -1184,7 +1368,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "copy-mode-mark-style",
|
{ .name = "copy-mode-mark-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "bg=red,fg=black",
|
.default_str = "bg=themeyellow,fg=themeblack",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the marked line in copy mode."
|
.text = "Style of the marked line in copy mode."
|
||||||
@@ -1223,7 +1407,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "copy-mode-current-line-number-style",
|
{ .name = "copy-mode-current-line-number-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "fg=yellow",
|
.default_str = "fg=themeyellow",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of current line number in copy mode."
|
.text = "Style of current line number in copy mode."
|
||||||
@@ -1232,7 +1416,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "copy-mode-line-number-style",
|
{ .name = "copy-mode-line-number-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "fg=white,dim",
|
.default_str = "fg=themelightgrey,dim",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of line numbers in copy mode."
|
.text = "Style of line numbers in copy mode."
|
||||||
@@ -1253,6 +1437,28 @@ const struct options_table_entry options_table[] = {
|
|||||||
.text = "Character used to fill unused parts of window."
|
.text = "Character used to fill unused parts of window."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "floating-pane-border-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
|
.default_str = "default",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Default border style for floating panes. "
|
||||||
|
"Overrides pane-border-style for floating panes unless "
|
||||||
|
"a per-pane style is set."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "floating-pane-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
|
.default_str = "default",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Default content style for floating panes. "
|
||||||
|
"Overrides window-style for floating panes unless "
|
||||||
|
"a per-pane style is set."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "main-pane-height",
|
{ .name = "main-pane-height",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
@@ -1281,7 +1487,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.default_str = "noattr,bg=yellow,fg=black",
|
.default_str = "noattr,bg=themeyellow,fg=themeblack",
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of indicators and highlighting in modes."
|
.text = "Style of indicators and highlighting in modes."
|
||||||
},
|
},
|
||||||
@@ -1330,7 +1536,9 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "pane-active-border-style",
|
{ .name = "pane-active-border-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
.default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}",
|
.default_str = "fg=#{?pane_marked,thememagenta,"
|
||||||
|
"#{?synchronize-panes,themered,"
|
||||||
|
"#{?pane_in_mode,themeyellow,themegreen}}}",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the active pane border."
|
.text = "Style of the active pane border."
|
||||||
@@ -1389,7 +1597,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "pane-border-style",
|
{ .name = "pane-border-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
.default_str = "default",
|
.default_str = "fg=themelightgrey",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the pane status lines."
|
.text = "Style of the pane status lines."
|
||||||
@@ -1408,13 +1616,23 @@ const struct options_table_entry options_table[] = {
|
|||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.choices = options_table_pane_scrollbars_list,
|
.choices = options_table_pane_scrollbars_list,
|
||||||
.default_num = PANE_SCROLLBARS_OFF,
|
.default_num = PANE_SCROLLBARS_OFF,
|
||||||
.text = "Pane scrollbar state."
|
.text = "Pane scrollbar state: off, on, modal, or auto-hide."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "pane-scrollbars-timeout",
|
||||||
|
.type = OPTIONS_TABLE_NUMBER,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.minimum = 0,
|
||||||
|
.maximum = INT_MAX,
|
||||||
|
.default_num = 500,
|
||||||
|
.unit = "milliseconds",
|
||||||
|
.text = "Time before modal and auto-hide pane scrollbars disappear."
|
||||||
},
|
},
|
||||||
|
|
||||||
{ .name = "pane-scrollbars-style",
|
{ .name = "pane-scrollbars-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
.default_str = "bg=black,fg=white,width=1,pad=0",
|
.default_str = "bg=themedarkgrey,fg=themelightgrey,width=1,pad=0",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the pane scrollbar."
|
.text = "Style of the pane scrollbar."
|
||||||
@@ -1431,7 +1649,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "popup-style",
|
{ .name = "popup-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "default",
|
.default_str = "bg=themedarkgrey,fg=themewhite",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Default style of popups."
|
.text = "Default style of popups."
|
||||||
@@ -1440,7 +1658,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "popup-border-style",
|
{ .name = "popup-border-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "default",
|
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Default style of popup borders."
|
.text = "Default style of popup borders."
|
||||||
@@ -1486,6 +1704,15 @@ const struct options_table_entry options_table[] = {
|
|||||||
"history when clearing the whole screen."
|
"history when clearing the whole screen."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "switch-mode-match-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
|
.default_str = "bg=cyan fg=black",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Style of matched characters in switch mode."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "synchronize-panes",
|
{ .name = "synchronize-panes",
|
||||||
.type = OPTIONS_TABLE_FLAG,
|
.type = OPTIONS_TABLE_FLAG,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
@@ -1503,6 +1730,15 @@ const struct options_table_entry options_table[] = {
|
|||||||
"A value of 0 means no limit."
|
"A value of 0 means no limit."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "tree-mode-border-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Style of borders in tree mode."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "tree-mode-preview-format",
|
{ .name = "tree-mode-preview-format",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
@@ -1518,13 +1754,22 @@ const struct options_table_entry options_table[] = {
|
|||||||
.default_str = "fg=#{?#{||:"
|
.default_str = "fg=#{?#{||:"
|
||||||
"#{&&:#{pane_format},#{pane_active}},"
|
"#{&&:#{pane_format},#{pane_active}},"
|
||||||
"#{&&:#{window_format},#{window_active}}},"
|
"#{&&:#{window_format},#{window_active}}},"
|
||||||
"#{display-panes-active-colour},"
|
"themered,"
|
||||||
"#{display-panes-colour}}",
|
"themeblue}",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of preview indicator in tree mode."
|
.text = "Style of preview indicator in tree mode."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "tree-mode-selection-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.default_str = "#{E:mode-style}",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Style of the selected line in tree mode."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "window-active-style",
|
{ .name = "window-active-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
@@ -1598,7 +1843,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "window-status-current-style",
|
{ .name = "window-status-current-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.default_str = "default",
|
.default_str = "underscore",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the current window in the status line."
|
.text = "Style of the current window in the status line."
|
||||||
|
|||||||
23
options.c
23
options.c
@@ -1214,6 +1214,17 @@ options_push_changes(const char *name)
|
|||||||
|
|
||||||
log_debug("%s: %s", __func__, name);
|
log_debug("%s: %s", __func__, name);
|
||||||
|
|
||||||
|
if (strcmp(name, "theme") == 0 ||
|
||||||
|
strncmp(name, "dark-theme-", 11) == 0 ||
|
||||||
|
strncmp(name, "light-theme-", 12) == 0) {
|
||||||
|
TAILQ_FOREACH(loop, &clients, entry) {
|
||||||
|
server_client_update_theme_colours(loop);
|
||||||
|
if (loop->tty.flags & TTY_OPENED)
|
||||||
|
tty_invalidate(&loop->tty);
|
||||||
|
server_redraw_client(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(name, "automatic-rename") == 0) {
|
if (strcmp(name, "automatic-rename") == 0) {
|
||||||
RB_FOREACH(w, windows, &windows) {
|
RB_FOREACH(w, windows, &windows) {
|
||||||
if (w->active == NULL)
|
if (w->active == NULL)
|
||||||
@@ -1253,6 +1264,7 @@ options_push_changes(const char *name)
|
|||||||
strcmp(name, "pane-border-lines") == 0 ||
|
strcmp(name, "pane-border-lines") == 0 ||
|
||||||
strcmp(name, "pane-border-status") == 0 ||
|
strcmp(name, "pane-border-status") == 0 ||
|
||||||
strcmp(name, "pane-scrollbars") == 0 ||
|
strcmp(name, "pane-scrollbars") == 0 ||
|
||||||
|
strcmp(name, "pane-scrollbars-timeout") == 0 ||
|
||||||
strcmp(name, "pane-scrollbars-position") == 0 ||
|
strcmp(name, "pane-scrollbars-position") == 0 ||
|
||||||
strcmp(name, "pane-scrollbars-style") == 0)
|
strcmp(name, "pane-scrollbars-style") == 0)
|
||||||
redraw_invalidate_all_scenes();
|
redraw_invalidate_all_scenes();
|
||||||
@@ -1274,8 +1286,17 @@ options_push_changes(const char *name)
|
|||||||
if (strcmp(name, "pane-border-status") == 0 ||
|
if (strcmp(name, "pane-border-status") == 0 ||
|
||||||
strcmp(name, "pane-scrollbars") == 0 ||
|
strcmp(name, "pane-scrollbars") == 0 ||
|
||||||
strcmp(name, "pane-scrollbars-position") == 0) {
|
strcmp(name, "pane-scrollbars-position") == 0) {
|
||||||
RB_FOREACH(w, windows, &windows)
|
RB_FOREACH(w, windows, &windows) {
|
||||||
|
w->sb = options_get_number(w->options,
|
||||||
|
"pane-scrollbars");
|
||||||
|
w->sb_pos = options_get_number(w->options,
|
||||||
|
"pane-scrollbars-position");
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strcmp(name, "pane-scrollbars") == 0) {
|
||||||
|
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
|
||||||
|
window_pane_scrollbar_hide(wp);
|
||||||
}
|
}
|
||||||
if (strcmp(name, "pane-scrollbars-style") == 0) {
|
if (strcmp(name, "pane-scrollbars-style") == 0) {
|
||||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
||||||
|
|||||||
4
paste.c
4
paste.c
@@ -219,7 +219,7 @@ paste_rename(const char *oldname, const char *newname, char **cause)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
name = clean_name(newname, "");
|
name = clean_name(newname, 0);
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
if (cause != NULL)
|
if (cause != NULL)
|
||||||
xasprintf(cause, "invalid buffer name: %s", newname);
|
xasprintf(cause, "invalid buffer name: %s", newname);
|
||||||
@@ -287,7 +287,7 @@ paste_set(char *data, size_t size, const char *name, char **cause)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
newname = clean_name(name, "");
|
newname = clean_name(name, 0);
|
||||||
if (newname == NULL) {
|
if (newname == NULL) {
|
||||||
if (cause != NULL)
|
if (cause != NULL)
|
||||||
xasprintf(cause, "invalid buffer name: %s", name);
|
xasprintf(cause, "invalid buffer name: %s", name);
|
||||||
|
|||||||
18
prompt.c
18
prompt.c
@@ -45,6 +45,7 @@ struct prompt {
|
|||||||
enum screen_cursor_style cstyle;
|
enum screen_cursor_style cstyle;
|
||||||
enum screen_cursor_style command_cstyle;
|
enum screen_cursor_style command_cstyle;
|
||||||
int ccolour;
|
int ccolour;
|
||||||
|
int command_ccolour;
|
||||||
int cmode;
|
int cmode;
|
||||||
int command_cmode;
|
int command_cmode;
|
||||||
|
|
||||||
@@ -97,6 +98,8 @@ prompt_flags_to_string(int flags)
|
|||||||
strlcat(tmp, "ISPANE,", sizeof tmp);
|
strlcat(tmp, "ISPANE,", sizeof tmp);
|
||||||
if (flags & PROMPT_ISMODE)
|
if (flags & PROMPT_ISMODE)
|
||||||
strlcat(tmp, "ISMODE,", sizeof tmp);
|
strlcat(tmp, "ISMODE,", sizeof tmp);
|
||||||
|
if (flags & PROMPT_EDITARROWS)
|
||||||
|
strlcat(tmp, "EDITARROWS,", sizeof tmp);
|
||||||
if (*tmp != '\0')
|
if (*tmp != '\0')
|
||||||
tmp[strlen(tmp) - 1] = '\0';
|
tmp[strlen(tmp) - 1] = '\0';
|
||||||
return (tmp);
|
return (tmp);
|
||||||
@@ -123,6 +126,8 @@ prompt_set_options(struct prompt_create_data *pd, struct session *s)
|
|||||||
screen_set_cursor_style(n, &pd->command_cstyle, &pd->command_cmode);
|
screen_set_cursor_style(n, &pd->command_cstyle, &pd->command_cmode);
|
||||||
style_apply(&gc, oo, "prompt-cursor-colour", NULL);
|
style_apply(&gc, oo, "prompt-cursor-colour", NULL);
|
||||||
pd->ccolour = gc.fg;
|
pd->ccolour = gc.fg;
|
||||||
|
style_apply(&gc, oo, "prompt-command-cursor-colour", NULL);
|
||||||
|
pd->command_ccolour = gc.fg;
|
||||||
pd->message_format = options_get_string(oo, "message-format");
|
pd->message_format = options_get_string(oo, "message-format");
|
||||||
pd->keys = options_get_number(oo, "status-keys");
|
pd->keys = options_get_number(oo, "status-keys");
|
||||||
pd->word_separators = options_get_string(oo, "word-separators");
|
pd->word_separators = options_get_string(oo, "word-separators");
|
||||||
@@ -177,6 +182,7 @@ prompt_create(const struct prompt_create_data *pd)
|
|||||||
pr->cstyle = pd->cstyle;
|
pr->cstyle = pd->cstyle;
|
||||||
pr->command_cstyle = pd->command_cstyle;
|
pr->command_cstyle = pd->command_cstyle;
|
||||||
pr->ccolour = pd->ccolour;
|
pr->ccolour = pd->ccolour;
|
||||||
|
pr->command_ccolour = pd->command_ccolour;
|
||||||
pr->cmode = pd->cmode;
|
pr->cmode = pd->cmode;
|
||||||
pr->command_cmode = pd->command_cmode;
|
pr->command_cmode = pd->command_cmode;
|
||||||
pr->message_format = xstrdup(pd->message_format);
|
pr->message_format = xstrdup(pd->message_format);
|
||||||
@@ -466,12 +472,13 @@ prompt_draw(struct prompt *pr, struct prompt_draw_data *pd)
|
|||||||
memcpy(&gc, &pr->command_style, sizeof gc);
|
memcpy(&gc, &pr->command_style, sizeof gc);
|
||||||
s->default_cstyle = pr->command_cstyle;
|
s->default_cstyle = pr->command_cstyle;
|
||||||
s->default_mode = pr->command_cmode;
|
s->default_mode = pr->command_cmode;
|
||||||
|
s->default_ccolour = pr->command_ccolour;
|
||||||
} else {
|
} else {
|
||||||
memcpy(&gc, &pr->style, sizeof gc);
|
memcpy(&gc, &pr->style, sizeof gc);
|
||||||
s->default_cstyle = pr->cstyle;
|
s->default_cstyle = pr->cstyle;
|
||||||
s->default_mode = pr->cmode;
|
s->default_mode = pr->cmode;
|
||||||
|
s->default_ccolour = pr->ccolour;
|
||||||
}
|
}
|
||||||
s->default_ccolour = pr->ccolour;
|
|
||||||
|
|
||||||
expanded = prompt_expand(pr);
|
expanded = prompt_expand(pr);
|
||||||
start = format_width(expanded);
|
start = format_width(expanded);
|
||||||
@@ -531,8 +538,6 @@ prompt_mouse(struct prompt *pr, u_int x, u_int ax, u_int aw, int *redraw)
|
|||||||
|
|
||||||
if (x < ax || x >= ax + aw)
|
if (x < ax || x >= ax + aw)
|
||||||
return (PROMPT_KEY_NOT_HANDLED);
|
return (PROMPT_KEY_NOT_HANDLED);
|
||||||
if (pr->flags & PROMPT_INCREMENTAL)
|
|
||||||
return (PROMPT_KEY_HANDLED);
|
|
||||||
|
|
||||||
start = prompt_width(pr, aw);
|
start = prompt_width(pr, aw);
|
||||||
left = aw - start;
|
left = aw - start;
|
||||||
@@ -1055,11 +1060,14 @@ prompt_check_move(struct prompt *pr, key_code key)
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
case KEYC_UP:
|
case KEYC_UP:
|
||||||
case KEYC_DOWN:
|
case KEYC_DOWN:
|
||||||
case KEYC_LEFT:
|
|
||||||
case KEYC_RIGHT:
|
|
||||||
case KEYC_PPAGE:
|
case KEYC_PPAGE:
|
||||||
case KEYC_NPAGE:
|
case KEYC_NPAGE:
|
||||||
break;
|
break;
|
||||||
|
case KEYC_LEFT:
|
||||||
|
case KEYC_RIGHT:
|
||||||
|
if (pr->flags & PROMPT_EDITARROWS)
|
||||||
|
return (PROMPT_KEY_NOT_HANDLED);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return (PROMPT_KEY_NOT_HANDLED);
|
return (PROMPT_KEY_NOT_HANDLED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,31 @@ TESTS!= echo *.sh
|
|||||||
.PHONY: all $(TESTS)
|
.PHONY: all $(TESTS)
|
||||||
.NOTPARALLEL: all $(TESTS)
|
.NOTPARALLEL: all $(TESTS)
|
||||||
|
|
||||||
all: $(TESTS)
|
all:
|
||||||
|
@failed=0; failures=; \
|
||||||
|
for test in $(TESTS); do \
|
||||||
|
printf '%-40s ' "$$test"; \
|
||||||
|
start=$$(date +%s); \
|
||||||
|
if sh "$$test" >/dev/null 2>&1; then \
|
||||||
|
end=$$(date +%s); \
|
||||||
|
echo "PASS ($$((end - start))s)"; \
|
||||||
|
else \
|
||||||
|
end=$$(date +%s); \
|
||||||
|
echo "FAIL ($$((end - start))s)"; \
|
||||||
|
failed=1; \
|
||||||
|
failures="$$failures $$test"; \
|
||||||
|
fi; \
|
||||||
|
sleep 1; \
|
||||||
|
done; \
|
||||||
|
if [ "$$failed" -ne 0 ]; then \
|
||||||
|
echo; \
|
||||||
|
echo "failures:"; \
|
||||||
|
for test in $$failures; do \
|
||||||
|
echo " $$test"; \
|
||||||
|
done; \
|
||||||
|
fi; \
|
||||||
|
exit $$failed
|
||||||
|
|
||||||
$(TESTS):
|
$(TESTS):
|
||||||
sh $*.sh
|
sh $@
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|||||||
Binary file not shown.
@@ -35,68 +35,59 @@ $TMUX set-option -qg allow-set-title on || exit 1
|
|||||||
$TMUX set-option -qg allow-rename on || exit 1
|
$TMUX set-option -qg allow-rename on || exit 1
|
||||||
$TMUX set-option -qg automatic-rename off || exit 1
|
$TMUX set-option -qg automatic-rename off || exit 1
|
||||||
|
|
||||||
# Commands reject ':' and '.' for sessions and windows, but allow '#'.
|
# Commands allow empty names, ':', '.', '#' and '#('.
|
||||||
$TMUX rename-session 'session#ok' || fail "session name with # rejected"
|
$TMUX rename-session '' || fail "empty session name rejected"
|
||||||
must_equal "$($TMUX display-message -p '#{session_name}')" 'session#ok'
|
must_equal "$($TMUX display-message -p '#{session_name}')" ''
|
||||||
must_fail $TMUX rename-session 'session:bad'
|
$TMUX rename-session 'session:.##(ok)' || \
|
||||||
must_fail $TMUX rename-session 'session.bad'
|
fail "session name with : . or #( rejected"
|
||||||
|
must_equal "$($TMUX display-message -p '#{session_name}')" 'session:.#(ok)'
|
||||||
|
|
||||||
$TMUX rename-window 'window#ok' || fail "window name with # rejected"
|
$TMUX rename-window '' || fail "empty window name rejected"
|
||||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'window#ok'
|
must_equal "$($TMUX display-message -p '#{window_name}')" ''
|
||||||
must_fail $TMUX rename-window 'window:bad'
|
$TMUX rename-window 'window:.##(ok)' || \
|
||||||
must_fail $TMUX rename-window 'window.bad'
|
fail "window name with : . or #( rejected"
|
||||||
|
must_equal "$($TMUX display-message -p '#{window_name}')" 'window:.#(ok)'
|
||||||
|
|
||||||
$TMUX set-option -q @name 'format#ok' || exit 1
|
$TMUX set-option -q @name 'format:.#(ok)' || exit 1
|
||||||
$TMUX rename-session '#{@name}' || fail "format in session name not expanded"
|
$TMUX rename-session '#{@name}' || fail "format in session name not expanded"
|
||||||
must_equal "$($TMUX display-message -p '#{session_name}')" 'format#ok'
|
must_equal "$($TMUX display-message -p '#{session_name}')" 'format:.#(ok)'
|
||||||
$TMUX rename-window '#{@name}' || fail "format in window name not expanded"
|
$TMUX rename-window '#{@name}' || fail "format in window name not expanded"
|
||||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'format#ok'
|
must_equal "$($TMUX display-message -p '#{window_name}')" 'format:.#(ok)'
|
||||||
must_fail $TMUX rename-session '#{session_name}:bad'
|
$TMUX set-option -q @name 'format:.#(ok)' || exit 1
|
||||||
must_fail $TMUX rename-window '#{window_name}.bad'
|
|
||||||
pid=$($TMUX display-message -p '#{pid}')
|
pid=$($TMUX display-message -p '#{pid}')
|
||||||
|
|
||||||
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
|
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
|
||||||
-s 'new-session#ok' -n 'new-window#ok') || \
|
-s 'new-session:.##(ok)' -n 'new-window:.##(ok)') || \
|
||||||
fail "new-session name with # rejected"
|
fail "new-session name with : . or #( rejected"
|
||||||
created_session=${created%:*}
|
created_session=${created%:*}
|
||||||
created_window=${created#*:}
|
created_window=${created#*:}
|
||||||
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
|
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
|
||||||
'new-session#ok'
|
'new-session:.#(ok)'
|
||||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||||
'new-window#ok'
|
'new-window:.#(ok)'
|
||||||
$TMUX kill-session -t "$created_session"
|
$TMUX kill-session -t "$created_session"
|
||||||
|
|
||||||
must_fail $TMUX new-session -d -s 'new-session:bad'
|
|
||||||
must_fail $TMUX new-session -d -s 'new-session.bad'
|
|
||||||
must_fail $TMUX new-session -d -n 'new-window:bad'
|
|
||||||
must_fail $TMUX new-session -d -n 'new-window.bad'
|
|
||||||
|
|
||||||
created_window=$($TMUX new-window -dP -F '#{window_id}' \
|
created_window=$($TMUX new-window -dP -F '#{window_id}' \
|
||||||
-n 'created-window#ok') || \
|
-n 'created-window:.##(ok)') || \
|
||||||
fail "new-window name with # rejected"
|
fail "new-window name with : . or #( rejected"
|
||||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||||
'created-window#ok'
|
'created-window:.#(ok)'
|
||||||
must_fail $TMUX new-window -d -n 'created-window:bad'
|
|
||||||
must_fail $TMUX new-window -d -n 'created-window.bad'
|
|
||||||
|
|
||||||
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
|
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
|
||||||
-s 'new-session-#{pid}' -n 'new-window-#{pid}') || \
|
-s 'new-session-#{pid}:.##(ok)' -n 'new-window-#{pid}:.##(ok)') || \
|
||||||
fail "format in new-session name not expanded"
|
fail "format in new-session name not expanded"
|
||||||
created_session=${created%:*}
|
created_session=${created%:*}
|
||||||
created_window=${created#*:}
|
created_window=${created#*:}
|
||||||
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
|
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
|
||||||
"new-session-$pid"
|
"new-session-$pid:.#(ok)"
|
||||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||||
"new-window-$pid"
|
"new-window-$pid:.#(ok)"
|
||||||
$TMUX kill-session -t "$created_session"
|
$TMUX kill-session -t "$created_session"
|
||||||
|
|
||||||
created_window=$($TMUX new-window -dP -F '#{window_id}' -n '#{@name}') || \
|
created_window=$($TMUX new-window -dP -F '#{window_id}' -n '#{@name}') || \
|
||||||
fail "format in new-window name not expanded"
|
fail "format in new-window name not expanded"
|
||||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||||
'format#ok'
|
'format:.#(ok)'
|
||||||
must_fail $TMUX new-session -d -s 'new-session-#{pid}:bad'
|
|
||||||
must_fail $TMUX new-session -d -n 'new-window-#{pid}.bad'
|
|
||||||
must_fail $TMUX new-window -d -n '#{window_name}:bad'
|
|
||||||
|
|
||||||
# Invalid UTF-8 is never allowed for command names.
|
# Invalid UTF-8 is never allowed for command names.
|
||||||
invalid=$(printf '\302')
|
invalid=$(printf '\302')
|
||||||
@@ -110,20 +101,26 @@ must_fail $TMUX set-buffer -b "bad${invalid}name" data
|
|||||||
# Titles set by commands allow '#', ':' and '.'.
|
# Titles set by commands allow '#', ':' and '.'.
|
||||||
$TMUX select-pane -T 'title#:.ok' || fail "command title rejected"
|
$TMUX select-pane -T 'title#:.ok' || fail "command title rejected"
|
||||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#:.ok'
|
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#:.ok'
|
||||||
|
$TMUX send-keys "printf '\\033]2;title#[fg=red]ok\\007'" Enter || exit 1
|
||||||
|
sleep 1
|
||||||
|
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#[fg=red]ok'
|
||||||
|
$TMUX send-keys "printf '\\033]2;title#(bad)\\007'" Enter || exit 1
|
||||||
|
sleep 1
|
||||||
|
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title_(bad)'
|
||||||
|
|
||||||
# Buffer names allow '#', ':' and '.'.
|
# Buffer names allow '#', ':' and '.'.
|
||||||
$TMUX set-buffer -b 'buffer#:.ok' data || fail "buffer name rejected"
|
$TMUX set-buffer -b 'buffer#:.ok' data || fail "buffer name rejected"
|
||||||
must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok'
|
must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok'
|
||||||
|
|
||||||
# Window names from escape sequences reject '#', ':' and '.' by cleaning them.
|
# Window names from escape sequences allow '#' except in '#('.
|
||||||
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
|
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
|
||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape___ok'
|
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#:.ok'
|
||||||
|
|
||||||
# Titles from escape sequences reject only '#'.
|
# Titles from escape sequences allow '#' except in '#('.
|
||||||
$TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1
|
$TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1
|
||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape_:.ok'
|
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape#:.ok'
|
||||||
|
|
||||||
# Invalid UTF-8 from escape sequences is ignored.
|
# Invalid UTF-8 from escape sequences is ignored.
|
||||||
$TMUX rename-window 'before-invalid' || exit 1
|
$TMUX rename-window 'before-invalid' || exit 1
|
||||||
|
|||||||
126
regress/environ-update.sh
Normal file
126
regress/environ-update.sh
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of update-environment handling (environ_update() in environ.c), which
|
||||||
|
# runs when a client attaches to a session: for each pattern in the session's
|
||||||
|
# update-environment option, a matching variable in the attaching client's
|
||||||
|
# environment is copied into the session environment, and a pattern that
|
||||||
|
# matches nothing clears that name in the session (a NULL-valued entry).
|
||||||
|
#
|
||||||
|
# This needs a real attached client with a controllable environment, so - as in
|
||||||
|
# format-variables.sh - a second server provides one: an inner "tmux attach"
|
||||||
|
# runs inside a pane of the second server, and the variables to import are set
|
||||||
|
# in that inner command's own environment.
|
||||||
|
#
|
||||||
|
# environ.sh covers the set-environment/show-environment commands themselves.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
# A second server on its own socket hosts the pane that runs the inner client.
|
||||||
|
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# check_value $var $expected
|
||||||
|
#
|
||||||
|
# Compare show-environment of $var on the session with $expected.
|
||||||
|
check_value()
|
||||||
|
{
|
||||||
|
out=$($TMUX show-environment -t main "$1" 2>&1)
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "show-environment $1 failed."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# wait_clients $n
|
||||||
|
#
|
||||||
|
# Wait (up to ~10s) until the test server has exactly $n clients attached.
|
||||||
|
wait_clients()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 10 ]; do
|
||||||
|
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||||
|
[ "$c" -eq "$1" ] && return 0
|
||||||
|
sleep 1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
|
||||||
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# The session imports MYVAR and ABSENTVAR by exact name and anything matching
|
||||||
|
# the glob TEST_*; nothing else is imported.
|
||||||
|
$TMUX set -g update-environment "MYVAR ABSENTVAR TEST_*" || exit 1
|
||||||
|
|
||||||
|
# Seed the session so the effect of attaching is visible: MYVAR will be
|
||||||
|
# overwritten by the client's value and ABSENTVAR will be cleared.
|
||||||
|
$TMUX set-environment -t main MYVAR oldvalue || exit 1
|
||||||
|
$TMUX set-environment -t main ABSENTVAR pre-existing || exit 1
|
||||||
|
|
||||||
|
# --- attach a client whose environment carries the imported variables ------
|
||||||
|
#
|
||||||
|
# MYVAR and TEST_GLOB are present in the inner client's environment; ABSENTVAR
|
||||||
|
# is deliberately absent; OTHER is present but not named by update-environment.
|
||||||
|
$TMUX2 new-session -d -x 90 -y 30 \
|
||||||
|
"MYVAR=fromclient TEST_GLOB=globval OTHER=nope $TMUX attach -t main" \
|
||||||
|
|| fail "could not start inner client"
|
||||||
|
wait_clients 1 || fail "no client attached to test server"
|
||||||
|
|
||||||
|
# MYVAR matched by name and present in the client -> imported (overwrites).
|
||||||
|
check_value MYVAR "MYVAR=fromclient"
|
||||||
|
# TEST_GLOB matched by the TEST_* glob and present -> imported.
|
||||||
|
check_value TEST_GLOB "TEST_GLOB=globval"
|
||||||
|
# ABSENTVAR named but not in the client environment -> cleared (NULL value,
|
||||||
|
# printed as -NAME).
|
||||||
|
check_value ABSENTVAR "-ABSENTVAR"
|
||||||
|
# OTHER is in the client environment but not named by update-environment, so it
|
||||||
|
# is not imported at all.
|
||||||
|
out=$($TMUX show-environment -t main OTHER 2>&1)
|
||||||
|
[ "$out" = "unknown variable: OTHER" ] || \
|
||||||
|
fail "OTHER was imported but should not have been: '$out'"
|
||||||
|
|
||||||
|
# --- -E disables the update-environment import -----------------------------
|
||||||
|
#
|
||||||
|
# Detach the client (kill its host server), reset the session variables, then
|
||||||
|
# reattach with -E: the session values must be left untouched.
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
wait_clients 0 || fail "client did not detach"
|
||||||
|
|
||||||
|
$TMUX set-environment -t main MYVAR oldvalue2 || exit 1
|
||||||
|
$TMUX set-environment -t main ABSENTVAR pre2 || exit 1
|
||||||
|
|
||||||
|
$TMUX2 new-session -d -x 90 -y 30 \
|
||||||
|
"MYVAR=fromclientE $TMUX attach -E -t main" \
|
||||||
|
|| fail "could not start inner -E client"
|
||||||
|
wait_clients 1 || fail "no -E client attached to test server"
|
||||||
|
|
||||||
|
# With -E neither variable is touched by the attach.
|
||||||
|
check_value MYVAR "MYVAR=oldvalue2"
|
||||||
|
check_value ABSENTVAR "ABSENTVAR=pre2"
|
||||||
|
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
fail "server died after update-environment tests"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
exit 0
|
||||||
187
regress/environ.sh
Normal file
187
regress/environ.sh
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of the environment engine (environ.c) and its two commands,
|
||||||
|
# set-environment/setenv (cmd-set-environment.c) and show-environment/showenv
|
||||||
|
# (cmd-show-environment.c).
|
||||||
|
#
|
||||||
|
# The environment is a red-black tree of name/value entries held at two
|
||||||
|
# scopes: the global environment and each session's environment. An entry
|
||||||
|
# may be marked hidden (ENVIRON_HIDDEN) or "cleared" (a NULL value, which
|
||||||
|
# masks an inherited variable rather than removing the entry). This
|
||||||
|
# exercises: set and show at global and session scope; the shell (-s) output
|
||||||
|
# form and its escaping of $ ` " and \; hidden variables (-h) and their
|
||||||
|
# filtering; -r cleared entries printed as -NAME / "unset NAME;"; -u removal;
|
||||||
|
# -F expansion of the value at set time; the plain "NAME=value" and "%hidden"
|
||||||
|
# config-file assignment forms (environ_put); and the full set of argument and
|
||||||
|
# target errors from both commands.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# check_value $args $expected
|
||||||
|
#
|
||||||
|
# Run show-environment with $args and compare the single-line output.
|
||||||
|
check_value()
|
||||||
|
{
|
||||||
|
out=$($TMUX show-environment $1 2>&1)
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "show-environment $1 failed."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ok()
|
||||||
|
{
|
||||||
|
if ! $TMUX "$@"; then
|
||||||
|
echo "Command failed (expected success): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# check_fail $expected_error $cmd...
|
||||||
|
check_fail()
|
||||||
|
{
|
||||||
|
exp="$1"
|
||||||
|
shift
|
||||||
|
out=$($TMUX "$@" 2>&1)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Command succeeded (expected failure): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Wrong error for: $*"
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server died: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# --- set and show at session scope ----------------------------------------
|
||||||
|
check_ok set-environment FOO bar
|
||||||
|
check_value "FOO" "FOO=bar"
|
||||||
|
# setenv is an alias for set-environment; showenv for show-environment.
|
||||||
|
check_ok setenv FOO2 bar2
|
||||||
|
out=$($TMUX showenv FOO2 2>&1)
|
||||||
|
[ "$out" = "FOO2=bar2" ] || { echo "setenv/showenv alias failed: '$out'"; exit 1; }
|
||||||
|
|
||||||
|
# --- set and show at global scope -----------------------------------------
|
||||||
|
#
|
||||||
|
# The global scope is separate from the session scope: a session variable is
|
||||||
|
# not visible in the global environment.
|
||||||
|
check_ok set-environment -g GVAR gval
|
||||||
|
check_value "-g GVAR" "GVAR=gval"
|
||||||
|
check_fail "unknown variable: FOO" show-environment -g FOO
|
||||||
|
|
||||||
|
# --- overwrite replaces the value -----------------------------------------
|
||||||
|
check_ok set-environment FOO baz
|
||||||
|
check_value "FOO" "FOO=baz"
|
||||||
|
|
||||||
|
# --- shell (-s) output form and escaping ----------------------------------
|
||||||
|
#
|
||||||
|
# With -s the value is printed as a shell assignment with export, and the
|
||||||
|
# characters $ ` " and \ are backslash-escaped (POSIX double-quote rules).
|
||||||
|
check_ok set-environment ESC 'a$b`c"d\e'
|
||||||
|
check_value "-s ESC" 'ESC="a\$b\`c\"d\\e"; export ESC;'
|
||||||
|
|
||||||
|
# --- -F expands the value as a format at set time -------------------------
|
||||||
|
#
|
||||||
|
# With a resolvable target the value is expanded once when set; the stored
|
||||||
|
# value is the result, not the format.
|
||||||
|
check_ok set-environment -t main -F EXP '#{session_name}'
|
||||||
|
check_value "EXP" "EXP=main"
|
||||||
|
|
||||||
|
# --- hidden variables (-h) ------------------------------------------------
|
||||||
|
#
|
||||||
|
# set-environment -h marks a variable hidden. show-environment hides it by
|
||||||
|
# default and only prints it when -h is given; conversely a normal variable is
|
||||||
|
# omitted when -h is given.
|
||||||
|
check_ok set-environment -h SECRET s3cr
|
||||||
|
check_value "SECRET" ""
|
||||||
|
check_value "-h SECRET" "SECRET=s3cr"
|
||||||
|
check_value "-h FOO" ""
|
||||||
|
|
||||||
|
# --- -r clears a variable (NULL value, masks inheritance) -----------------
|
||||||
|
#
|
||||||
|
# A cleared entry still exists but has no value: normal form prints "-NAME"
|
||||||
|
# and shell form prints "unset NAME;".
|
||||||
|
check_ok set-environment -r FOO
|
||||||
|
check_value "FOO" "-FOO"
|
||||||
|
check_value "-s FOO" "unset FOO;"
|
||||||
|
|
||||||
|
# --- -u removes a variable entirely ---------------------------------------
|
||||||
|
check_ok set-environment -u FOO
|
||||||
|
check_fail "unknown variable: FOO" show-environment FOO
|
||||||
|
|
||||||
|
# --- show with no name lists every (non-hidden) variable ------------------
|
||||||
|
check_ok set-environment -g LISTA 1
|
||||||
|
check_ok set-environment -g LISTB 2
|
||||||
|
check_ok set-environment -gh LISTHID 3
|
||||||
|
out=$($TMUX show-environment -g 2>&1)
|
||||||
|
echo "$out" | grep -q '^LISTA=1$' || { echo "list missing LISTA"; exit 1; }
|
||||||
|
echo "$out" | grep -q '^LISTB=2$' || { echo "list missing LISTB"; exit 1; }
|
||||||
|
# A hidden variable is not listed without -h.
|
||||||
|
echo "$out" | grep -q '^LISTHID' && { echo "list showed hidden var without -h"; exit 1; }
|
||||||
|
# With -h only hidden variables are listed.
|
||||||
|
$TMUX show-environment -gh 2>&1 | grep -q '^LISTHID=3$' || \
|
||||||
|
{ echo "list -h missing LISTHID"; exit 1; }
|
||||||
|
|
||||||
|
# --- config-file assignment forms (environ_put) ---------------------------
|
||||||
|
#
|
||||||
|
# A bare NAME=value line in a config file sets a global variable; a "%hidden"
|
||||||
|
# NAME=value line sets a hidden one. Start a second server from such a config
|
||||||
|
# and read the values back.
|
||||||
|
CONF=$(mktemp)
|
||||||
|
cat > "$CONF" <<EOF
|
||||||
|
CFGVAR=fromconfig
|
||||||
|
%hidden CFGHID=hiddencfg
|
||||||
|
EOF
|
||||||
|
CTMUX="$TEST_TMUX -Ltest2 -f$CONF"
|
||||||
|
$CTMUX kill-server 2>/dev/null
|
||||||
|
$CTMUX new-session -d -s c -x 80 -y 24 || { rm -f "$CONF"; exit 1; }
|
||||||
|
out=$($CTMUX show-environment -g CFGVAR 2>&1)
|
||||||
|
[ "$out" = "CFGVAR=fromconfig" ] || \
|
||||||
|
{ echo "config assignment failed: '$out'"; $CTMUX kill-server; rm -f "$CONF"; exit 1; }
|
||||||
|
out=$($CTMUX show-environment -gh CFGHID 2>&1)
|
||||||
|
[ "$out" = "CFGHID=hiddencfg" ] || \
|
||||||
|
{ echo "config %hidden failed: '$out'"; $CTMUX kill-server; rm -f "$CONF"; exit 1; }
|
||||||
|
# The %hidden variable is hidden from a plain show.
|
||||||
|
out=$($CTMUX show-environment -g CFGHID 2>&1)
|
||||||
|
[ "$out" = "" ] || \
|
||||||
|
{ echo "config %hidden not hidden: '$out'"; $CTMUX kill-server; rm -f "$CONF"; exit 1; }
|
||||||
|
$CTMUX kill-server 2>/dev/null
|
||||||
|
rm -f "$CONF"
|
||||||
|
|
||||||
|
# --- set-environment argument errors --------------------------------------
|
||||||
|
check_fail "empty variable name" set-environment "" x
|
||||||
|
check_fail "variable name contains =" set-environment "A=B" x
|
||||||
|
check_fail "can't specify a value with -u" set-environment -u FOO val
|
||||||
|
check_fail "can't specify a value with -r" set-environment -r FOO val
|
||||||
|
check_fail "no value specified" set-environment NOVAL
|
||||||
|
|
||||||
|
# --- show-environment errors ----------------------------------------------
|
||||||
|
check_fail "unknown variable: MISSING" show-environment MISSING
|
||||||
|
|
||||||
|
# --- unresolvable target errors -------------------------------------------
|
||||||
|
check_fail "no such session: nosuch" show-environment -t nosuch FOO
|
||||||
|
check_fail "no such session: nosuch" set-environment -t nosuch FOO bar
|
||||||
|
|
||||||
|
assert_alive "after environ tests"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
106
regress/format-fuzzy.sh
Normal file
106
regress/format-fuzzy.sh
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of fuzzy format matching.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
|
||||||
|
# test_format $format $expected_result
|
||||||
|
test_format()
|
||||||
|
{
|
||||||
|
fmt="$1"
|
||||||
|
exp="$2"
|
||||||
|
|
||||||
|
out=$($TMUX display-message -p "$fmt")
|
||||||
|
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Fuzzy format test failed for '$fmt'."
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX -f/dev/null new-session -d || exit 1
|
||||||
|
|
||||||
|
# Empty pattern matches everything but has no highlighted positions.
|
||||||
|
test_format '#{m/z:,abc}' '1'
|
||||||
|
test_format '#{m/p:,abc}' ''
|
||||||
|
|
||||||
|
# Plain fuzzy matching and positions.
|
||||||
|
test_format '#{m/z:abc,a_b_c}' '1'
|
||||||
|
test_format '#{m/p:abc,a_b_c}' '0,2,4'
|
||||||
|
test_format '#{m/z:abc,acb}' '0'
|
||||||
|
test_format '#{m/p:abc,acb}' ''
|
||||||
|
|
||||||
|
# Multiple terms are ANDed. Positions are for all positive matched terms.
|
||||||
|
test_format '#{m/z:dev bash,dev:1 bash}' '1'
|
||||||
|
test_format '#{m/p:dev bash,dev:1 bash}' '0,1,2,6,7,8,9'
|
||||||
|
test_format '#{m/z:dev bash,dev:1 sh}' '0'
|
||||||
|
test_format '#{m/p:dev bash,dev:1 sh}' ''
|
||||||
|
|
||||||
|
# Lowercase patterns are case-insensitive for ASCII; uppercase makes the
|
||||||
|
# pattern case-sensitive.
|
||||||
|
test_format '#{m/z:abc,ABC}' '1'
|
||||||
|
test_format '#{m/p:abc,ABC}' '0,1,2'
|
||||||
|
test_format '#{m/z:ABC,abc}' '0'
|
||||||
|
test_format '#{m/p:ABC,abc}' ''
|
||||||
|
|
||||||
|
# Exact substring matching with a leading quote.
|
||||||
|
test_format "#{m/z:'bash,dev bash}" '1'
|
||||||
|
test_format "#{m/p:'bash,dev bash}" '4,5,6,7'
|
||||||
|
test_format "#{m/z:'bash,b-a-s-h}" '0'
|
||||||
|
test_format "#{m/p:'bash,b-a-s-h}" ''
|
||||||
|
|
||||||
|
# Prefix and suffix matching.
|
||||||
|
test_format '#{m/z:^dev,dev bash}' '1'
|
||||||
|
test_format '#{m/p:^dev,dev bash}' '0,1,2'
|
||||||
|
test_format '#{m/z:^dev,prod dev}' '0'
|
||||||
|
test_format '#{m/p:^dev,prod dev}' ''
|
||||||
|
test_format '#{m/z:bash$,dev bash}' '1'
|
||||||
|
test_format '#{m/p:bash$,dev bash}' '4,5,6,7'
|
||||||
|
test_format '#{m/z:bash$,bash dev}' '0'
|
||||||
|
test_format '#{m/p:bash$,bash dev}' ''
|
||||||
|
|
||||||
|
# Inverse terms. Plain inverse terms are exact substring tests, not fuzzy.
|
||||||
|
test_format '#{m/z:!ssh,dev bash}' '1'
|
||||||
|
test_format '#{m/z:!ssh,dev ssh}' '0'
|
||||||
|
test_format '#{m/z:!ssh,s_s_h}' '1'
|
||||||
|
test_format '#{m/z:dev !ssh,dev bash}' '1'
|
||||||
|
test_format '#{m/p:dev !ssh,dev bash}' '0,1,2'
|
||||||
|
test_format '#{m/z:dev !ssh,dev ssh}' '0'
|
||||||
|
test_format '#{m/p:dev !ssh,dev ssh}' ''
|
||||||
|
|
||||||
|
# OR separates groups; each group is an AND of its terms.
|
||||||
|
test_format '#{m/z:prod | dev,dev bash}' '1'
|
||||||
|
test_format '#{m/p:prod | dev,dev bash}' '0,1,2'
|
||||||
|
test_format '#{m/z:prod | dev,prod bash}' '1'
|
||||||
|
test_format '#{m/p:prod | dev,prod bash}' '0,1,2,3'
|
||||||
|
test_format '#{m/z:prod | dev,test bash}' '0'
|
||||||
|
test_format '#{m/p:prod | dev,test bash}' ''
|
||||||
|
|
||||||
|
# Style sequences do not count towards display positions.
|
||||||
|
test_format '#{m/z:dev,#[bold]dev#[default]}' '1'
|
||||||
|
test_format '#{m/p:dev,#[bold]dev#[default]}' '0,1,2'
|
||||||
|
test_format '#{m/p:dev,#[fg=red#,bg=blue]dev#[default]}' '0,1,2'
|
||||||
|
test_format '#{m/p:dev,#[bold]d#[default]e#[underscore]v#[default]}' '0,1,2'
|
||||||
|
test_format '#{m/p:bash,#[bold]dev#[default] bash}' '4,5,6,7'
|
||||||
|
|
||||||
|
# UTF-8. Non-ASCII matching is exact, even when ASCII would fold case.
|
||||||
|
test_format '#{m/z:é,café}' '1'
|
||||||
|
test_format '#{m/p:é,café}' '3'
|
||||||
|
test_format '#{m/z:é,É}' '0'
|
||||||
|
test_format '#{m/p:é,É}' ''
|
||||||
|
test_format '#{m/z:éx,éx}' '1'
|
||||||
|
test_format '#{m/p:éx,éx}' '0,1'
|
||||||
|
|
||||||
|
# Wide UTF-8 characters occupy two display cells and both should be marked for
|
||||||
|
# highlighting.
|
||||||
|
test_format '#{m/z:界,a界b}' '1'
|
||||||
|
test_format '#{m/p:界,a界b}' '1,2'
|
||||||
|
|
||||||
|
exit 0
|
||||||
563
regress/format-modifiers.sh
Normal file
563
regress/format-modifiers.sh
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of format modifiers as described in tmux(1) FORMATS.
|
||||||
|
#
|
||||||
|
# This complements format-strings.sh (which covers escapes, conditionals,
|
||||||
|
# boolean operators and the l: literal modifier). Here we exercise the
|
||||||
|
# remaining modifiers: comparisons/matching (m, C, <, >, ==, ...), numeric
|
||||||
|
# operations (e|op|), width/padding/truncation (=, p, n, w, a, R), basename
|
||||||
|
# and dirname (b, d), time conversion (t), loops (S, W, P), colour (c) and
|
||||||
|
# modifier nesting/limits.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
TZ=UTC
|
||||||
|
export TZ
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
|
||||||
|
ESC=$(printf '\033')
|
||||||
|
|
||||||
|
# test_format $format $expected [$target]
|
||||||
|
#
|
||||||
|
# Expand $format with display-message and compare with $expected. If $target
|
||||||
|
# is given it is passed to display-message with -t.
|
||||||
|
test_format()
|
||||||
|
{
|
||||||
|
fmt="$1"
|
||||||
|
exp="$2"
|
||||||
|
target="$3"
|
||||||
|
|
||||||
|
if [ -n "$target" ]; then
|
||||||
|
out=$($TMUX display-message -t "$target" -p "$fmt")
|
||||||
|
else
|
||||||
|
out=$($TMUX display-message -p "$fmt")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Format test failed for '$fmt'."
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# test_expand $format $expected
|
||||||
|
#
|
||||||
|
# Expand $format in a plain format_expand context (list-windows -F on the
|
||||||
|
# single-window "tf" session) rather than the format_expand_time context of
|
||||||
|
# display-message. This matters for t/f: display-message runs the whole format
|
||||||
|
# through strftime(3), so a strftime specifier there must be doubled (%%H); in a
|
||||||
|
# format_expand context a single specifier (%H) is applied directly to the
|
||||||
|
# variable's time.
|
||||||
|
test_expand()
|
||||||
|
{
|
||||||
|
fmt="$1"
|
||||||
|
exp="$2"
|
||||||
|
|
||||||
|
out=$($TMUX list-windows -t tf -F "$fmt")
|
||||||
|
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Format test failed for '$fmt'."
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# assert_alive
|
||||||
|
#
|
||||||
|
# Check that the server is still responding (used after operations that could
|
||||||
|
# in principle crash it, such as recursion and division by zero).
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server did not survive: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# Single-window session used by test_expand for format_expand-context tests.
|
||||||
|
$TMUX new-session -d -s tf || exit 1
|
||||||
|
|
||||||
|
# User options used as inputs. Modifiers operate on variable names, so plain
|
||||||
|
# literals must be provided via options (or a nested #{l:...}). They are set
|
||||||
|
# globally (-g) so they are visible from every session, including the "tf"
|
||||||
|
# session used by test_expand.
|
||||||
|
$TMUX set -g @s 'abcdefghij' || exit 1
|
||||||
|
$TMUX set -g @path '/usr/local/bin/foo' || exit 1
|
||||||
|
$TMUX set -g @name 'window-name' || exit 1
|
||||||
|
$TMUX set -g @greek 'αβγ' || exit 1 # 6 bytes, 3 columns wide
|
||||||
|
$TMUX set -g @cjk '中文' || exit 1 # 6 bytes, 4 columns wide
|
||||||
|
$TMUX set -g @host 'myhost' || exit 1
|
||||||
|
$TMUX set -g @ts '1000000000' || exit 1 # 2001-09-09 01:46:40 UTC
|
||||||
|
$TMUX set -g @sp 'a b$c' || exit 1 # shell-special characters for q:
|
||||||
|
$TMUX set -g @hash 'a#b' || exit 1 # a "#" for q/e:
|
||||||
|
|
||||||
|
|
||||||
|
# --- Comparisons and matching --------------------------------------------
|
||||||
|
|
||||||
|
# m: glob match, first argument is the pattern.
|
||||||
|
test_format "#{m:*foo*,barfoobar}" "1"
|
||||||
|
test_format "#{m:*foo*,barbar}" "0"
|
||||||
|
test_format "#{m:abc,abc}" "1"
|
||||||
|
# m/i: ignore case.
|
||||||
|
test_format "#{m/i:*FOO*,barfoobar}" "1"
|
||||||
|
test_format "#{m/i:*FOO*,barbar}" "0"
|
||||||
|
# m/r: regular expression.
|
||||||
|
test_format "#{m/r:^[0-9]+\$,12345}" "1"
|
||||||
|
test_format "#{m/r:^[0-9]+\$,12a45}" "0"
|
||||||
|
# m/ri: regular expression, ignore case.
|
||||||
|
test_format "#{m/ri:^ab+\$,ABBB}" "1"
|
||||||
|
test_format "#{m/ri:^ab+\$,ACCC}" "0"
|
||||||
|
# m/z: fuzzy match, returns a boolean.
|
||||||
|
test_format "#{m/z:foo,foobar}" "1"
|
||||||
|
test_format "#{m/z:xyz,foobar}" "0"
|
||||||
|
# m/p: fuzzy match, returns the matched (0-based) column positions.
|
||||||
|
test_format "#{m/p:ac,abc}" "0,2"
|
||||||
|
test_format "#{m/p:xyz,abc}" ""
|
||||||
|
# Fuzzy match against empty text.
|
||||||
|
test_format "#{m/p:x,}" ""
|
||||||
|
test_format "#{m/z:x,}" "0"
|
||||||
|
|
||||||
|
# String comparisons.
|
||||||
|
test_format "#{==:#{@host},myhost}" "1"
|
||||||
|
test_format "#{==:#{@host},other}" "0"
|
||||||
|
test_format "#{!=:abc,xyz}" "1"
|
||||||
|
test_format "#{!=:abc,abc}" "0"
|
||||||
|
test_format "#{<:3,5}" "1"
|
||||||
|
test_format "#{<:5,3}" "0"
|
||||||
|
test_format "#{>:5,3}" "1"
|
||||||
|
test_format "#{>:3,5}" "0"
|
||||||
|
test_format "#{<=:5,5}" "1"
|
||||||
|
test_format "#{<=:6,5}" "0"
|
||||||
|
test_format "#{>=:5,5}" "1"
|
||||||
|
test_format "#{>=:4,5}" "0"
|
||||||
|
|
||||||
|
# Negation and canonical boolean.
|
||||||
|
test_format "#{!:0}" "1"
|
||||||
|
test_format "#{!:1}" "0"
|
||||||
|
test_format "#{!!:}" "0"
|
||||||
|
test_format "#{!!:0}" "0"
|
||||||
|
test_format "#{!!:non-empty}" "1"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Quoting (q) ---------------------------------------------------------
|
||||||
|
|
||||||
|
# q: escapes shell special characters with a backslash.
|
||||||
|
test_format "#{q:@sp}" 'a\ b\$c'
|
||||||
|
# q/e and q/h escape "#" for the format/style parser by doubling it.
|
||||||
|
test_format "#{q/e:@hash}" 'a##b'
|
||||||
|
test_format "#{q/h:@hash}" 'a##b'
|
||||||
|
# q/a quotes the value as a single shell argument.
|
||||||
|
test_format "#{q/a:@sp}" '"a b\$c"'
|
||||||
|
|
||||||
|
|
||||||
|
# --- Name existence (N) --------------------------------------------------
|
||||||
|
|
||||||
|
# N/w is true if a window with the (expanded) name exists in the session, N/s
|
||||||
|
# if a session with that name exists. The default (no argument) is /w.
|
||||||
|
$TMUX rename-window -t main:0 knownwin
|
||||||
|
test_format "#{N/s:main}" "1"
|
||||||
|
test_format "#{N/s:nosuchsession}" "0"
|
||||||
|
test_format "#{N/w:knownwin}" "1" "main:"
|
||||||
|
test_format "#{N/w:nosuchwindow}" "0" "main:"
|
||||||
|
test_format "#{N:nosuchwindow}" "0" "main:"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Numeric operations (e) ----------------------------------------------
|
||||||
|
|
||||||
|
# Integer operators.
|
||||||
|
test_format "#{e|+|:2,3}" "5"
|
||||||
|
test_format "#{e|-|:10,4}" "6"
|
||||||
|
test_format "#{e|-|:2,5}" "-3"
|
||||||
|
test_format "#{e|*|:6,7}" "42"
|
||||||
|
test_format "#{e|/|:20,4}" "5"
|
||||||
|
# Modulus - both spellings (% must be doubled as it is a strftime specifier).
|
||||||
|
test_format "#{e|m|:7,3}" "1"
|
||||||
|
test_format "#{e|%%|:7,3}" "1"
|
||||||
|
|
||||||
|
# Numeric comparison operators.
|
||||||
|
test_format "#{e|==|:5,5}" "1"
|
||||||
|
test_format "#{e|!=|:5,5}" "0"
|
||||||
|
test_format "#{e|<|:2,5}" "1"
|
||||||
|
test_format "#{e|>|:9,2}" "1"
|
||||||
|
test_format "#{e|<=|:5,5}" "1"
|
||||||
|
test_format "#{e|>=|:5,5}" "1"
|
||||||
|
|
||||||
|
# Floating point with a decimal-place count.
|
||||||
|
test_format "#{e|*|f|4:5.5,3}" "16.5000"
|
||||||
|
test_format "#{e|/|f|3:1,3}" "0.333"
|
||||||
|
test_format "#{e|/|f|2:10,3}" "3.33"
|
||||||
|
# Default number of decimal places for float is two.
|
||||||
|
test_format "#{e|*|f:2.5,2}" "5.00"
|
||||||
|
|
||||||
|
# Division by zero must not crash the server (result is unspecified).
|
||||||
|
$TMUX display-message -p "#{e|/|:5,0}" >/dev/null 2>&1
|
||||||
|
$TMUX display-message -p "#{e|/|f:5,0}" >/dev/null 2>&1
|
||||||
|
assert_alive "division by zero"
|
||||||
|
|
||||||
|
|
||||||
|
# --- ASCII and repeat ----------------------------------------------------
|
||||||
|
|
||||||
|
# a: numeric value to its ASCII character.
|
||||||
|
test_format "#{a:98}" "b"
|
||||||
|
test_format "#{a:65}" "A"
|
||||||
|
# a: out-of-range or non-numeric input yields an empty string.
|
||||||
|
test_format "#{a:200}" ""
|
||||||
|
test_format "#{a:notanumber}" ""
|
||||||
|
# R: repeat first argument second-argument times.
|
||||||
|
test_format "#{R:a,3}" "aaa"
|
||||||
|
test_format "#{R:ab,2}" "abab"
|
||||||
|
# A long repeat exercises output-buffer growth during expansion.
|
||||||
|
test_format "#{n:#{R:x,300}}" "300"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Width, padding and truncation ---------------------------------------
|
||||||
|
|
||||||
|
# =N truncates from the start, =-N from the end.
|
||||||
|
test_format "#{=5:@s}" "abcde"
|
||||||
|
test_format "#{=-5:@s}" "fghij"
|
||||||
|
# = with no width, or a non-numeric width, does not truncate.
|
||||||
|
test_format "#{=:@s}" "abcdefghij"
|
||||||
|
test_format "#{=/x:@s}" "abcdefghij"
|
||||||
|
# A marker is appended/prepended only when trimming actually occurs.
|
||||||
|
test_format "#{=/5/...:@s}" "abcde..."
|
||||||
|
test_format "#{=/5/...:@name}" "windo..."
|
||||||
|
test_format "#{=/20/...:@s}" "abcdefghij"
|
||||||
|
# Truncation is display-width (UTF-8) aware: a wide (2-column) character is only
|
||||||
|
# included if it fits entirely within the limit.
|
||||||
|
test_format "#{=3:@greek}" "αβγ"
|
||||||
|
test_format "#{=2:@greek}" "αβ"
|
||||||
|
test_format "#{=2:@cjk}" "中"
|
||||||
|
test_format "#{=1:@cjk}" ""
|
||||||
|
# Markers with wide characters: the marker is added when trimming occurs, and a
|
||||||
|
# limit that splits a wide character drops it entirely.
|
||||||
|
test_format "#{=/2/x:@cjk}" "中x"
|
||||||
|
test_format "#{=/1/x:@cjk}" "x"
|
||||||
|
|
||||||
|
# p pads to a width: a positive width left-aligns (pads on the right), a
|
||||||
|
# negative width right-aligns (pads on the left).
|
||||||
|
test_format "#{p12:@name}" "window-name "
|
||||||
|
test_format "#{p-12:@name}" " window-name"
|
||||||
|
# No padding once the value already meets the width.
|
||||||
|
test_format "#{p3:@name}" "window-name"
|
||||||
|
# p with no width does nothing.
|
||||||
|
test_format "#{p:@name}" "window-name"
|
||||||
|
# Padding is display-width aware: @cjk is 4 columns wide, so p6/p-6 add two
|
||||||
|
# spaces (not four).
|
||||||
|
test_format "#{p6:@cjk}" "中文 "
|
||||||
|
test_format "#{p-6:@cjk}" " 中文"
|
||||||
|
|
||||||
|
# n is byte length, w is display width.
|
||||||
|
test_format "#{n:@s}" "10"
|
||||||
|
test_format "#{w:@s}" "10"
|
||||||
|
test_format "#{n:@greek}" "6"
|
||||||
|
test_format "#{w:@greek}" "3"
|
||||||
|
test_format "#{n:@cjk}" "6"
|
||||||
|
test_format "#{w:@cjk}" "4"
|
||||||
|
|
||||||
|
|
||||||
|
# --- basename and dirname ------------------------------------------------
|
||||||
|
|
||||||
|
test_format "#{b:@path}" "foo"
|
||||||
|
test_format "#{d:@path}" "/usr/local/bin"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Time conversion -----------------------------------------------------
|
||||||
|
|
||||||
|
# t: converts an integer time to a ctime(3) string.
|
||||||
|
test_format "#{t:@ts}" "Sun Sep 9 01:46:40 2001"
|
||||||
|
# t/p: shorter format for times in the past.
|
||||||
|
test_format "#{t/p:@ts}" "Sep01"
|
||||||
|
# t/r: relative time depends on the current time, just check it is non-empty.
|
||||||
|
if [ -z "$($TMUX display-message -p '#{t/r:@ts}')" ]; then
|
||||||
|
echo "Format test failed for '#{t/r:@ts}': empty result"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# t/f: custom strftime format applied to the variable's time. Tested in a
|
||||||
|
# format_expand context (list-windows -F), where a single strftime specifier is
|
||||||
|
# applied directly. (In display-message, which additionally expands the format
|
||||||
|
# through strftime, these would need to be doubled - %%Y etc.) The colon in the
|
||||||
|
# format is escaped as '#:' because it is otherwise the modifier separator.
|
||||||
|
test_expand "#{t/f/%Y:@ts}" "2001"
|
||||||
|
test_expand "#{t/f/%Y-%m-%d:@ts}" "2001-09-09"
|
||||||
|
test_expand "#{t/f/%H#:%M#:%S:@ts}" "01:46:40"
|
||||||
|
# An escaped comma in the custom format is unescaped before strftime.
|
||||||
|
test_expand "#{t/f/%Y#,end:@ts}" "2001,end"
|
||||||
|
|
||||||
|
# T: expands its argument and then runs the result through strftime with the
|
||||||
|
# current time. A value with no strftime specifier is returned unchanged.
|
||||||
|
test_format "#{T:@ts}" "1000000000"
|
||||||
|
|
||||||
|
# t/p (pretty) and t/r (relative) format times by age relative to now, with a
|
||||||
|
# different branch per age band. Build options a known number of seconds in the
|
||||||
|
# past and check each yields a non-empty result (the exact text depends on the
|
||||||
|
# wall clock, so only non-emptiness is asserted).
|
||||||
|
now=$(date +%s)
|
||||||
|
for age in 30 300 4000 90000 200000 3000000 40000000; do
|
||||||
|
$TMUX set -g @age "$((now - age))"
|
||||||
|
if [ -z "$($TMUX display-message -p '#{t/r:@age}')" ]; then
|
||||||
|
echo "Empty #{t/r:@age} for age ${age}s"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$($TMUX display-message -p '#{t/p:@age}')" ]; then
|
||||||
|
echo "Empty #{t/p:@age} for age ${age}s"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# A time in the future has no relative form.
|
||||||
|
$TMUX set -g @future "$((now + 100000))"
|
||||||
|
test_format "#{t/r:@future}" ""
|
||||||
|
|
||||||
|
|
||||||
|
# --- Content search (C) --------------------------------------------------
|
||||||
|
|
||||||
|
# Use a window running cat so the content is deterministic (no shell prompt).
|
||||||
|
$TMUX new-session -d -s search -x 80 -y 10 'cat'
|
||||||
|
sleep 1
|
||||||
|
$TMUX send-keys -t search: 'Zebra_Marker_42' Enter
|
||||||
|
sleep 1
|
||||||
|
# C: returns the (1-based) line number of a match or 0 if not found.
|
||||||
|
test_format "#{C:Zebra_Marker_42}" "1" "search:"
|
||||||
|
test_format "#{C:Absent_String_999}" "0" "search:"
|
||||||
|
test_format "#{C/r:Zebra_.*_42}" "1" "search:"
|
||||||
|
test_format "#{C/i:zebra_marker_42}" "1" "search:"
|
||||||
|
$TMUX kill-session -t search 2>/dev/null
|
||||||
|
|
||||||
|
|
||||||
|
# --- Colour (c) ----------------------------------------------------------
|
||||||
|
|
||||||
|
# c: converts a colour to its six-digit hexadecimal RGB value.
|
||||||
|
test_format "#{c:red}" "800000"
|
||||||
|
test_format "#{c:colour4}" "000080"
|
||||||
|
test_format "#{c:#7f7f7f}" "7f7f7f"
|
||||||
|
# c/f and c/b produce the SGR escape sequence for fg/bg respectively.
|
||||||
|
test_format "#{c/f:red}" "${ESC}[31m"
|
||||||
|
test_format "#{c/b:red}" "${ESC}[41m"
|
||||||
|
test_format "#{c/b:colour4}" "${ESC}[48;5;4m"
|
||||||
|
# "none" gives a reset; an unknown colour gives an empty string.
|
||||||
|
test_format "#{c/f:none}" "${ESC}[0m"
|
||||||
|
test_format "#{c:notacolour}" ""
|
||||||
|
test_format "#{c/f:notacolour}" ""
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nesting and limits --------------------------------------------------
|
||||||
|
|
||||||
|
# Modifier chaining: inner b: then outer truncation/padding/length.
|
||||||
|
test_format "#{=5:#{b:@path}}" "foo"
|
||||||
|
test_format "#{=2:#{b:@path}}" "fo"
|
||||||
|
test_format "#{p6:#{b:@path}}" "foo "
|
||||||
|
test_format "#{n:#{b:@path}}" "3"
|
||||||
|
|
||||||
|
# Nested l: literal expanded then truncated.
|
||||||
|
test_format "#{=5:#{l:abcdefghij}}" "abcde"
|
||||||
|
|
||||||
|
# Deeper nesting: basename -> pad to 10 -> truncate to 5.
|
||||||
|
test_format "#{=5:#{p10:#{b:@path}}}" "foo "
|
||||||
|
# A substitution applied to a nested basename.
|
||||||
|
test_format "#{s/o/O/:#{b:@path}}" "fOO"
|
||||||
|
|
||||||
|
# Unbounded self-recursion must hit the loop limit rather than crash.
|
||||||
|
$TMUX set @rec '#{E:@rec}'
|
||||||
|
$TMUX display-message -p '#{E:@rec}' >/dev/null 2>&1
|
||||||
|
assert_alive "recursive expansion"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Missing, malformed and limit inputs ---------------------------------
|
||||||
|
|
||||||
|
# An undefined variable expands to empty; modifiers on it behave sensibly.
|
||||||
|
test_format "#{@undefined}" ""
|
||||||
|
test_format "#{=5:@undefined}" ""
|
||||||
|
test_format "#{b:@undefined}" ""
|
||||||
|
test_format "#{n:@undefined}" "0"
|
||||||
|
|
||||||
|
# Malformed numeric expressions expand to empty rather than erroring out.
|
||||||
|
test_format "#{e|+|:notanumber,2}" "" # invalid left operand
|
||||||
|
test_format "#{e|+|:2,notanumber}" "" # invalid right operand
|
||||||
|
test_format "#{e|badop|:1,2}" "" # unknown operator
|
||||||
|
test_format "#{e|+|f|x:1,2}" "" # invalid precision
|
||||||
|
test_format "#{e|+|:1}" "" # too few operands
|
||||||
|
test_format "#{e|+|f|2|extra:1,2}" "" # too many arguments (limit is 3)
|
||||||
|
|
||||||
|
# Repeat with a non-numeric or zero count yields an empty string.
|
||||||
|
test_format "#{R:a,notanumber}" ""
|
||||||
|
test_format "#{R:a,0}" ""
|
||||||
|
|
||||||
|
# Comparisons with too few arguments expand to empty.
|
||||||
|
test_format "#{==:a}" ""
|
||||||
|
test_format "#{<:a}" ""
|
||||||
|
|
||||||
|
# A substitution with fewer than two arguments is a no-op.
|
||||||
|
test_format "#{s/onlyone:@s}" "abcdefghij"
|
||||||
|
|
||||||
|
# A non-numeric width for = or p is treated as no width (no change).
|
||||||
|
test_format "#{=/x:@s}" "abcdefghij"
|
||||||
|
test_format "#{p/x:@s}" "abcdefghij"
|
||||||
|
|
||||||
|
# The I (client terminal) modifier with no attached client is empty; this also
|
||||||
|
# exercises its argument parsing (/c termcap, /f feature, default). The
|
||||||
|
# non-empty terminal cases are covered with a real client in format-variables.sh.
|
||||||
|
test_format "#{I/c:RGB}" ""
|
||||||
|
test_format "#{I/f:overline}" ""
|
||||||
|
test_format "#{I:x}" ""
|
||||||
|
|
||||||
|
|
||||||
|
# --- Escaping inside modifiers -------------------------------------------
|
||||||
|
|
||||||
|
# A "," or "#" inside a modifier argument is escaped with "#".
|
||||||
|
test_format "#{s/#,/-/:#{l:a,b,c}}" "a-b-c" # escaped comma in the pattern
|
||||||
|
test_format "#{=/3/#,:@s}" "abc," # escaped comma in the marker
|
||||||
|
# The truncation marker is itself expanded as a format.
|
||||||
|
test_format "#{=/3/#{l:>}:@s}" "abc>"
|
||||||
|
|
||||||
|
# Substitution flags: a third argument of "i" is case-insensitive; an invalid
|
||||||
|
# regular expression leaves the text unchanged.
|
||||||
|
test_format "#{s/A/X/i:@s}" "Xbcdefghij"
|
||||||
|
test_format "#{s/[/X/:@s}" "abcdefghij"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Unicode in modifier arguments ---------------------------------------
|
||||||
|
|
||||||
|
# Wide (CJK) and emoji text: matching, substitution, repeat and markers all
|
||||||
|
# operate on characters, and n/w report bytes/columns.
|
||||||
|
$TMUX set -g @emoji '😀😀' || exit 1 # 8 bytes, 4 columns
|
||||||
|
test_format "#{m:*中*,#{@cjk}}" "1"
|
||||||
|
test_format "#{s/文/X/:@cjk}" "中X"
|
||||||
|
test_format "#{R:中,3}" "中中中"
|
||||||
|
test_format "#{=/1/中:@s}" "a中"
|
||||||
|
test_format "#{w:@emoji}" "4"
|
||||||
|
test_format "#{n:@emoji}" "8"
|
||||||
|
test_format "#{=2:@emoji}" "😀"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Server messages (show-messages) -------------------------------------
|
||||||
|
|
||||||
|
# show-messages formats each logged message (this exercises the message-time
|
||||||
|
# formatting path); just check the server survives producing it.
|
||||||
|
$TMUX show-messages >/dev/null 2>&1
|
||||||
|
assert_alive "show-messages"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Verbose expansion (logging) -----------------------------------------
|
||||||
|
|
||||||
|
# display-message -v turns on format logging, so re-expanding a representative
|
||||||
|
# set of formats with -v exercises the logging code paths. Only survival is
|
||||||
|
# checked; the log text itself is not asserted.
|
||||||
|
for f in \
|
||||||
|
'#{=3:@s}' \
|
||||||
|
'#{e|+|:2,3}' \
|
||||||
|
'#{e|*|f|2:2.5,2}' \
|
||||||
|
'#{m:*a*,abc}' \
|
||||||
|
'#{<:3,5}' \
|
||||||
|
'#{s/a/X/:@s}' \
|
||||||
|
'#{b:@path}' \
|
||||||
|
'#{t:@ts}' \
|
||||||
|
'#{p6:@name}' \
|
||||||
|
'#{=3:#{b:@path}}'; do
|
||||||
|
$TMUX display-message -v -p "$f" >/dev/null 2>&1
|
||||||
|
done
|
||||||
|
assert_alive "verbose expansion"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Loops and sorting (S, W, P, L) --------------------------------------
|
||||||
|
#
|
||||||
|
# These need a fully controlled server so the set of sessions, windows and
|
||||||
|
# panes (and their order) is known, so start from a clean server. This must be
|
||||||
|
# the last section as it discards the setup above.
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# Sessions, created in this order, so session ids (and hence creation order)
|
||||||
|
# are zeta=$0, alpha=$1, mike=$2.
|
||||||
|
$TMUX new-session -d -s zeta -x 80 -y 24 || exit 1
|
||||||
|
$TMUX new-session -d -s alpha || exit 1
|
||||||
|
$TMUX new-session -d -s mike || exit 1
|
||||||
|
$TMUX set -g automatic-rename off
|
||||||
|
|
||||||
|
# S loops over every session. The default order is by session id (SORT_INDEX),
|
||||||
|
# /i is the same, /n is by name, and the r suffix reverses.
|
||||||
|
test_format "#{S:#{session_name} }" "zeta alpha mike "
|
||||||
|
test_format "#{S/i:#{session_name} }" "zeta alpha mike "
|
||||||
|
test_format "#{S/n:#{session_name} }" "alpha mike zeta "
|
||||||
|
test_format "#{S/nr:#{session_name} }" "zeta mike alpha "
|
||||||
|
test_format "#{S/ir:#{session_name} }" "mike alpha zeta "
|
||||||
|
# /t sorts by activity time; the exact order is timing-dependent, so just check
|
||||||
|
# every session is still iterated (this exercises the activity-sort branch).
|
||||||
|
test_format "#{S/t:x}" "xxx"
|
||||||
|
# An unrecognised sort letter falls back to the default order; /r on its own
|
||||||
|
# reverses that default (this covers the fall-through branch).
|
||||||
|
test_format "#{S/r:#{session_name} }" "mike alpha zeta "
|
||||||
|
|
||||||
|
# Windows in session zeta: window 0 renamed charlie, then alpha at 1, bravo at
|
||||||
|
# 2. The default order is by index (SORT_ORDER), /n is by name, r reverses.
|
||||||
|
$TMUX rename-window -t zeta:0 charlie
|
||||||
|
$TMUX new-window -d -t zeta:1 -n alpha
|
||||||
|
$TMUX new-window -d -t zeta:2 -n bravo
|
||||||
|
test_format "#{W:#{window_name} }" "charlie alpha bravo " "zeta:"
|
||||||
|
test_format "#{W/n:#{window_name} }" "alpha bravo charlie " "zeta:"
|
||||||
|
test_format "#{W/nr:#{window_name} }" "charlie bravo alpha " "zeta:"
|
||||||
|
test_format "#{W/ir:#{window_index}}" "210" "zeta:"
|
||||||
|
# /i (by index) and /t (by activity); /i matches the default order here.
|
||||||
|
test_format "#{W/i:#{window_name} }" "charlie alpha bravo " "zeta:"
|
||||||
|
test_format "#{W/t:x}" "xxx" "zeta:"
|
||||||
|
# An unrecognised sort letter falls back to the default order; /r reverses it.
|
||||||
|
test_format "#{W/r:#{window_name} }" "bravo alpha charlie " "zeta:"
|
||||||
|
|
||||||
|
# Panes in window zeta:charlie. Splitting the active (newest) pane each time
|
||||||
|
# makes pane index match creation order (0,1,2 left to right). The default
|
||||||
|
# order is by creation (SORT_CREATION), r reverses.
|
||||||
|
$TMUX split-window -h -t zeta:charlie
|
||||||
|
$TMUX split-window -h -t zeta:charlie
|
||||||
|
test_format "#{P:#{pane_index}}" "012" "zeta:charlie"
|
||||||
|
test_format "#{P/r:#{pane_index}}" "210" "zeta:charlie"
|
||||||
|
# A pane-sort argument is accepted; for panes only the r (reverse) suffix has an
|
||||||
|
# effect, so these all keep the count and exercise the argument branch.
|
||||||
|
test_format "#{P/i:x}" "xxx" "zeta:charlie"
|
||||||
|
test_format "#{P/n:x}" "xxx" "zeta:charlie"
|
||||||
|
test_format "#{P/t:x}" "xxx" "zeta:charlie"
|
||||||
|
|
||||||
|
# Verbose expansion of the loops, to exercise their logging paths.
|
||||||
|
$TMUX display-message -v -p "#{S:#{session_name}}" >/dev/null 2>&1
|
||||||
|
$TMUX display-message -v -t zeta: -p "#{W:#{window_name}}" >/dev/null 2>&1
|
||||||
|
$TMUX display-message -v -t zeta:charlie -p "#{P:#{pane_index}}" >/dev/null 2>&1
|
||||||
|
assert_alive "verbose loop expansion"
|
||||||
|
|
||||||
|
# L loops over attached clients. Attach two control-mode clients, each held
|
||||||
|
# open by a background process keeping a FIFO's write end open.
|
||||||
|
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||||
|
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||||
|
rm -f "$FIFO1" "$FIFO2"
|
||||||
|
mkfifo "$FIFO1" "$FIFO2" || exit 1
|
||||||
|
# Hold the write ends open so the control clients stay attached.
|
||||||
|
sleep 30 >"$FIFO1" &
|
||||||
|
HOLD1=$!
|
||||||
|
sleep 30 >"$FIFO2" &
|
||||||
|
HOLD2=$!
|
||||||
|
$TMUX -C attach -t zeta <"$FIFO1" >/dev/null 2>&1 &
|
||||||
|
CC1=$!
|
||||||
|
$TMUX -C attach -t alpha <"$FIFO2" >/dev/null 2>&1 &
|
||||||
|
CC2=$!
|
||||||
|
sleep 1
|
||||||
|
# Two clients attached: L emits one item per client.
|
||||||
|
test_format "#{L:x}" "xx"
|
||||||
|
# The client sort orders (default, index, name, activity, reversed) are all
|
||||||
|
# accepted; assert only the count so the test does not depend on client names or
|
||||||
|
# timing.
|
||||||
|
test_format "#{L/i:x}" "xx"
|
||||||
|
test_format "#{L/n:x}" "xx"
|
||||||
|
test_format "#{L/t:x}" "xx"
|
||||||
|
test_format "#{L/nr:x}" "xx"
|
||||||
|
test_format "#{L/r:x}" "xx"
|
||||||
|
# Now detach one and confirm the count drops to one.
|
||||||
|
kill $HOLD2 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
test_format "#{L:x}" "x"
|
||||||
|
kill $HOLD1 $CC1 $CC2 2>/dev/null
|
||||||
|
rm -f "$FIFO1" "$FIFO2"
|
||||||
|
|
||||||
|
exit 0
|
||||||
143
regress/format-mouse.sh
Normal file
143
regress/format-mouse.sh
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of the mouse format variables (mouse_x, mouse_y, mouse_word,
|
||||||
|
# mouse_line, ...). These are only populated while a mouse key binding is being
|
||||||
|
# dispatched, so the test drives a real mouse event:
|
||||||
|
#
|
||||||
|
# - an inner client is attached inside a pane of a second ("outer") tmux
|
||||||
|
# server, giving the inner server a genuine terminal;
|
||||||
|
# - mouse mode is on and a MouseDown1Pane binding records the mouse format
|
||||||
|
# variables into an option;
|
||||||
|
# - an SGR mouse sequence is written to the outer pane, so the inner client
|
||||||
|
# receives it as a real mouse click.
|
||||||
|
#
|
||||||
|
# This exercises the mouse callbacks and the grid word/line lookup code that
|
||||||
|
# display-message cannot otherwise reach.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server >/dev/null 2>&1
|
||||||
|
$TMUX2 kill-server >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# click COL ROW
|
||||||
|
#
|
||||||
|
# Write an SGR mouse press then release (button 0) at 1-based COL/ROW to the
|
||||||
|
# outer pane holding the inner client.
|
||||||
|
click()
|
||||||
|
{
|
||||||
|
col="$1"
|
||||||
|
row="$2"
|
||||||
|
seq=$(printf '\033[<0;%s;%sM\033[<0;%s;%sm' "$col" "$row" "$col" "$row")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
# Inner session with a single pane running cat, so its content is exactly what
|
||||||
|
# we send it.
|
||||||
|
$TMUX new-session -d -s cov -x 80 -y 24 'cat' || exit 1
|
||||||
|
$TMUX set -g mouse on
|
||||||
|
sleep 1
|
||||||
|
$TMUX send-keys -t cov:0.0 'alpha beta gamma' Enter
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Record every pane mouse variable when the pane is clicked.
|
||||||
|
$TMUX bind -n MouseDown1Pane run-shell \
|
||||||
|
"$TMUX set -g @m 'x=#{mouse_x} y=#{mouse_y} word=#{mouse_word} line=#{mouse_line} pane=#{mouse_pane} hl=[#{mouse_hyperlink}]'"
|
||||||
|
|
||||||
|
# Attach a real client inside an outer tmux pane. Clicks all target the first
|
||||||
|
# row, which lines up with the inner client regardless of the outer status line.
|
||||||
|
$TMUX2 new-session -d -x 80 -y 24 "$TMUX attach -t cov" || exit 1
|
||||||
|
sleep 1
|
||||||
|
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||||
|
[ -n "$OUTER" ] || fail "No outer pane."
|
||||||
|
|
||||||
|
# Click column 3, row 1: over the first word ("alpha") of the first line.
|
||||||
|
click 3 1
|
||||||
|
|
||||||
|
M=$($TMUX show -gv @m 2>/dev/null)
|
||||||
|
[ -n "$M" ] || fail "Mouse binding did not fire (no @m)."
|
||||||
|
|
||||||
|
# mouse_x is 0-based column (SGR column 3 -> x 2); mouse_y is 0-based row 0.
|
||||||
|
case "$M" in
|
||||||
|
*"x=2 "*) ;;
|
||||||
|
*) fail "Unexpected mouse_x in: $M" ;;
|
||||||
|
esac
|
||||||
|
case "$M" in
|
||||||
|
*"y=0 "*) ;;
|
||||||
|
*) fail "Unexpected mouse_y in: $M" ;;
|
||||||
|
esac
|
||||||
|
# mouse_word is the word under the cursor, mouse_line the whole line.
|
||||||
|
case "$M" in
|
||||||
|
*"word=alpha "*) ;;
|
||||||
|
*) fail "Unexpected mouse_word in: $M" ;;
|
||||||
|
esac
|
||||||
|
case "$M" in
|
||||||
|
*"line=alpha beta gamma "*) ;;
|
||||||
|
*) fail "Unexpected mouse_line in: $M" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# A click in a different column selects a different word.
|
||||||
|
click 8 1
|
||||||
|
M=$($TMUX show -gv @m 2>/dev/null)
|
||||||
|
case "$M" in
|
||||||
|
*"word=beta "*) ;;
|
||||||
|
*) fail "Unexpected mouse_word for second click in: $M" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# The same variables have a separate path when the pane is in a mode (the word
|
||||||
|
# and line come from the mode, not the live grid). A binding in the copy-mode
|
||||||
|
# key table fires while copy mode is active.
|
||||||
|
$TMUX bind -T copy-mode MouseDown1Pane run-shell \
|
||||||
|
"$TMUX set -g @cm 'x=#{mouse_x} word=#{mouse_word} line=#{mouse_line}'"
|
||||||
|
$TMUX copy-mode -t cov:0.0
|
||||||
|
sleep 1
|
||||||
|
click 8 1
|
||||||
|
CM=$($TMUX show -gv @cm 2>/dev/null)
|
||||||
|
case "$CM" in
|
||||||
|
*"word=beta"*) ;;
|
||||||
|
*) fail "Unexpected copy-mode mouse_word in: $CM" ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -t cov:0.0 -X cancel
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Hyperlinks: a new window whose pane emits an OSC 8 hyperlink over the text
|
||||||
|
# "LINKED". Clicking it reports the target URL via mouse_hyperlink (this drives
|
||||||
|
# the grid hyperlink lookup). The emitter is written to a small script to keep
|
||||||
|
# the escape sequence readable.
|
||||||
|
LINKSH="${TMPDIR:-/tmp}/fmt-mouse-link-$$.sh"
|
||||||
|
cat >"$LINKSH" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
printf '\033]8;;http://example.com\033\\LINKED\033]8;;\033\\\n'
|
||||||
|
exec cat
|
||||||
|
EOF
|
||||||
|
chmod +x "$LINKSH"
|
||||||
|
$TMUX neww -t cov: -n link "$LINKSH"
|
||||||
|
sleep 1
|
||||||
|
$TMUX select-window -t cov:link
|
||||||
|
sleep 1
|
||||||
|
click 3 1
|
||||||
|
M=$($TMUX show -gv @m 2>/dev/null)
|
||||||
|
rm -f "$LINKSH"
|
||||||
|
case "$M" in
|
||||||
|
*"hl=[http://example.com]"*) ;;
|
||||||
|
*) fail "Unexpected mouse_hyperlink in: $M" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
exit 0
|
||||||
388
regress/format-variables.sh
Normal file
388
regress/format-variables.sh
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests that every format variable listed in tmux(1) (the format_table in
|
||||||
|
# format.c) can be expanded without crashing the server, and checks the value
|
||||||
|
# of a stable subset.
|
||||||
|
#
|
||||||
|
# The main point is coverage and crash-safety: each variable is expanded in a
|
||||||
|
# rich context - a real attached client (from a nested tmux), a control-mode
|
||||||
|
# client, a grouped session, two windows with a bell alert, a window with two
|
||||||
|
# panes running cat, a paste buffer and options - so the per-variable callbacks
|
||||||
|
# actually run. format-modifiers.sh covers the modifier machinery; this covers
|
||||||
|
# the variable callbacks.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
# A second server on its own socket provides a real terminal (an inner client
|
||||||
|
# attached inside one of its panes) so client terminal variables are populated.
|
||||||
|
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||||
|
|
||||||
|
# Every variable name in format_table[]. Kept as a plain word list so it can be
|
||||||
|
# iterated with normal shell word splitting.
|
||||||
|
NAMES="
|
||||||
|
active_window_index
|
||||||
|
alternate_on
|
||||||
|
alternate_saved_x
|
||||||
|
alternate_saved_y
|
||||||
|
bracket_paste_flag
|
||||||
|
buffer_created
|
||||||
|
buffer_full
|
||||||
|
buffer_mode_format
|
||||||
|
buffer_name
|
||||||
|
buffer_sample
|
||||||
|
buffer_size
|
||||||
|
client_activity
|
||||||
|
client_cell_height
|
||||||
|
client_cell_width
|
||||||
|
client_colours
|
||||||
|
client_control_mode
|
||||||
|
client_created
|
||||||
|
client_discarded
|
||||||
|
client_flags
|
||||||
|
client_height
|
||||||
|
client_key_table
|
||||||
|
client_last_session
|
||||||
|
client_mode_format
|
||||||
|
client_name
|
||||||
|
client_pid
|
||||||
|
client_prefix
|
||||||
|
client_readonly
|
||||||
|
client_session
|
||||||
|
client_termfeatures
|
||||||
|
client_termname
|
||||||
|
client_termtype
|
||||||
|
client_theme
|
||||||
|
client_tty
|
||||||
|
client_uid
|
||||||
|
client_user
|
||||||
|
client_utf8
|
||||||
|
client_width
|
||||||
|
client_written
|
||||||
|
config_files
|
||||||
|
cursor_blinking
|
||||||
|
cursor_character
|
||||||
|
cursor_colour
|
||||||
|
cursor_flag
|
||||||
|
cursor_shape
|
||||||
|
cursor_very_visible
|
||||||
|
cursor_x
|
||||||
|
cursor_y
|
||||||
|
history_all_bytes
|
||||||
|
history_bytes
|
||||||
|
history_limit
|
||||||
|
history_size
|
||||||
|
host
|
||||||
|
host_short
|
||||||
|
insert_flag
|
||||||
|
keypad_cursor_flag
|
||||||
|
keypad_flag
|
||||||
|
last_window_index
|
||||||
|
mouse_all_flag
|
||||||
|
mouse_any_flag
|
||||||
|
mouse_button_flag
|
||||||
|
mouse_hyperlink
|
||||||
|
mouse_line
|
||||||
|
mouse_pane
|
||||||
|
mouse_sgr_flag
|
||||||
|
mouse_standard_flag
|
||||||
|
mouse_status_line
|
||||||
|
mouse_status_range
|
||||||
|
mouse_utf8_flag
|
||||||
|
mouse_word
|
||||||
|
mouse_x
|
||||||
|
mouse_y
|
||||||
|
next_session_id
|
||||||
|
origin_flag
|
||||||
|
pane_active
|
||||||
|
pane_at_bottom
|
||||||
|
pane_at_left
|
||||||
|
pane_at_right
|
||||||
|
pane_at_top
|
||||||
|
pane_bg
|
||||||
|
pane_bottom
|
||||||
|
pane_current_command
|
||||||
|
pane_current_path
|
||||||
|
pane_dead
|
||||||
|
pane_dead_signal
|
||||||
|
pane_dead_status
|
||||||
|
pane_dead_time
|
||||||
|
pane_fg
|
||||||
|
pane_flags
|
||||||
|
pane_floating_flag
|
||||||
|
pane_format
|
||||||
|
pane_height
|
||||||
|
pane_id
|
||||||
|
pane_in_mode
|
||||||
|
pane_index
|
||||||
|
pane_input_off
|
||||||
|
pane_key_mode
|
||||||
|
pane_last
|
||||||
|
pane_left
|
||||||
|
pane_marked
|
||||||
|
pane_marked_set
|
||||||
|
pane_mode
|
||||||
|
pane_path
|
||||||
|
pane_pb_progress
|
||||||
|
pane_pb_state
|
||||||
|
pane_pid
|
||||||
|
pane_pipe
|
||||||
|
pane_pipe_pid
|
||||||
|
pane_right
|
||||||
|
pane_search_string
|
||||||
|
pane_start_command
|
||||||
|
pane_start_path
|
||||||
|
pane_synchronized
|
||||||
|
pane_tabs
|
||||||
|
pane_title
|
||||||
|
pane_top
|
||||||
|
pane_tty
|
||||||
|
pane_unseen_changes
|
||||||
|
pane_width
|
||||||
|
pane_x
|
||||||
|
pane_y
|
||||||
|
pane_z
|
||||||
|
pane_zoomed_flag
|
||||||
|
pid
|
||||||
|
scroll_region_lower
|
||||||
|
scroll_region_upper
|
||||||
|
server_sessions
|
||||||
|
session_active
|
||||||
|
session_activity
|
||||||
|
session_activity_flag
|
||||||
|
session_alert
|
||||||
|
session_alerts
|
||||||
|
session_attached
|
||||||
|
session_attached_list
|
||||||
|
session_bell_flag
|
||||||
|
session_created
|
||||||
|
session_format
|
||||||
|
session_group
|
||||||
|
session_group_attached
|
||||||
|
session_group_attached_list
|
||||||
|
session_group_list
|
||||||
|
session_group_many_attached
|
||||||
|
session_group_size
|
||||||
|
session_grouped
|
||||||
|
session_id
|
||||||
|
session_last_attached
|
||||||
|
session_many_attached
|
||||||
|
session_marked
|
||||||
|
session_name
|
||||||
|
session_path
|
||||||
|
session_silence_flag
|
||||||
|
session_stack
|
||||||
|
session_windows
|
||||||
|
sixel_support
|
||||||
|
socket_path
|
||||||
|
start_time
|
||||||
|
synchronized_output_flag
|
||||||
|
tree_mode_format
|
||||||
|
uid
|
||||||
|
user
|
||||||
|
version
|
||||||
|
window_active
|
||||||
|
window_active_clients
|
||||||
|
window_active_clients_list
|
||||||
|
window_active_sessions
|
||||||
|
window_active_sessions_list
|
||||||
|
window_activity
|
||||||
|
window_activity_flag
|
||||||
|
window_bell_flag
|
||||||
|
window_bigger
|
||||||
|
window_cell_height
|
||||||
|
window_cell_width
|
||||||
|
window_end_flag
|
||||||
|
window_flags
|
||||||
|
window_format
|
||||||
|
window_height
|
||||||
|
window_id
|
||||||
|
window_index
|
||||||
|
window_last_flag
|
||||||
|
window_layout
|
||||||
|
window_linked
|
||||||
|
window_linked_sessions
|
||||||
|
window_linked_sessions_list
|
||||||
|
window_marked_flag
|
||||||
|
window_name
|
||||||
|
window_offset_x
|
||||||
|
window_offset_y
|
||||||
|
window_panes
|
||||||
|
window_raw_flags
|
||||||
|
window_silence_flag
|
||||||
|
window_stack_index
|
||||||
|
window_start_flag
|
||||||
|
window_visible_layout
|
||||||
|
window_width
|
||||||
|
window_zoomed_flag
|
||||||
|
wrap_flag
|
||||||
|
"
|
||||||
|
|
||||||
|
# test_var $name $expected [$extra_args...]
|
||||||
|
#
|
||||||
|
# Expand a single #{name} and compare against $expected. Any extra arguments
|
||||||
|
# are passed straight to display-message (e.g. -c or -t).
|
||||||
|
test_var()
|
||||||
|
{
|
||||||
|
name="$1"
|
||||||
|
exp="$2"
|
||||||
|
shift 2
|
||||||
|
|
||||||
|
out=$($TMUX display-message "$@" -p "#{$name}")
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Variable test failed for '#{$name}'."
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server did not survive: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
FIFO="${TMPDIR:-/tmp}/fmt-vars-$$"
|
||||||
|
HOLD=""
|
||||||
|
CC=""
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
[ -n "$HOLD" ] && kill $HOLD 2>/dev/null
|
||||||
|
[ -n "$CC" ] && kill $CC 2>/dev/null
|
||||||
|
rm -f "$FIFO"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# A session "cov" with a window "win0" holding two panes running cat, plus a
|
||||||
|
# second window, an option and a paste buffer.
|
||||||
|
$TMUX new-session -d -s cov -x 80 -y 24 -n win0 'cat' || exit 1
|
||||||
|
$TMUX set -g automatic-rename off
|
||||||
|
$TMUX set -g monitor-bell on
|
||||||
|
$TMUX set -g monitor-activity on
|
||||||
|
$TMUX split-window -t cov:win0 -d 'cat' || exit 1
|
||||||
|
$TMUX new-window -d -t cov:1 -n win1 'cat' || exit 1
|
||||||
|
$TMUX set -g @opt 'optionvalue' || exit 1
|
||||||
|
$TMUX set-buffer -b buf0 'somebuffer' || exit 1
|
||||||
|
|
||||||
|
# A second session grouped with cov, so the session_group_* variables have real
|
||||||
|
# data to report.
|
||||||
|
$TMUX new-session -d -s cov2 -t cov || exit 1
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
$TMUX send-keys -t cov:win0.0 'some pane content' Enter
|
||||||
|
# Ring the bell in the non-current window so a bell alert is raised on the
|
||||||
|
# session (this populates session_alert/session_alerts and window_bell_flag).
|
||||||
|
$TMUX send-keys -t cov:win1.0 C-g
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Attach a control-mode client, held open by a background process keeping the
|
||||||
|
# write end of a FIFO open, so client_* variables have a client to read.
|
||||||
|
rm -f "$FIFO"
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
sleep 30 >"$FIFO" &
|
||||||
|
HOLD=$!
|
||||||
|
$TMUX -C attach -t cov <"$FIFO" >/dev/null 2>&1 &
|
||||||
|
CC=$!
|
||||||
|
|
||||||
|
# Attach a real client too: an inner tmux running inside a pane of the second
|
||||||
|
# server gets a genuine terminal, which populates the terminal-dependent client
|
||||||
|
# variables (client_termname, cursor_shape, the I modifier, ...).
|
||||||
|
$TMUX2 new-session -d -x 90 -y 30 "$TMUX attach -t cov" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# The real (terminal) client, identified by not being in control mode.
|
||||||
|
RC=$($TMUX list-clients -F '#{client_control_mode} #{client_name}' |
|
||||||
|
awk '$1==0 { print $2; exit }')
|
||||||
|
# The control client.
|
||||||
|
CLIENT=$($TMUX list-clients -F '#{client_control_mode} #{client_name}' |
|
||||||
|
awk '$1==1 { print $2; exit }')
|
||||||
|
[ -n "$RC" ] || fail "No real client attached."
|
||||||
|
[ -n "$CLIENT" ] || fail "No control client attached."
|
||||||
|
|
||||||
|
# Expand every variable at once, with the real terminal client and a target
|
||||||
|
# pane in context, and confirm the server survives. This runs every callback.
|
||||||
|
FMT=""
|
||||||
|
for n in $NAMES; do
|
||||||
|
FMT="$FMT#{$n}"
|
||||||
|
done
|
||||||
|
$TMUX display-message -c "$RC" -t cov:win0.0 -p "$FMT" >/dev/null 2>&1
|
||||||
|
assert_alive "expanding all variables together"
|
||||||
|
|
||||||
|
# Expand each variable on its own too, so a crash can be pinned to one name.
|
||||||
|
for n in $NAMES; do
|
||||||
|
$TMUX display-message -c "$RC" -t cov:win0.0 -p "#{$n}" >/dev/null 2>&1
|
||||||
|
assert_alive "expanding #{$n}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Deterministic checks on stable variables (targeting pane 0 of window 0).
|
||||||
|
TGT="cov:win0.0"
|
||||||
|
test_var session_name "cov" -t "$TGT"
|
||||||
|
test_var window_name "win0" -t "$TGT"
|
||||||
|
test_var window_index "0" -t "$TGT"
|
||||||
|
test_var window_panes "2" -t "$TGT"
|
||||||
|
test_var session_windows "2" -t "$TGT"
|
||||||
|
test_var pane_index "0" -t "$TGT"
|
||||||
|
test_var pane_in_mode "0" -t "$TGT"
|
||||||
|
test_var pane_at_top "1" -t "$TGT"
|
||||||
|
test_var pane_at_left "1" -t "$TGT"
|
||||||
|
test_var last_window_index "1" -t "$TGT"
|
||||||
|
test_var pid "$($TMUX display-message -p '#{pid}')" -t "$TGT"
|
||||||
|
|
||||||
|
# The grouped session is reported as such.
|
||||||
|
test_var session_grouped "1" -t "cov:"
|
||||||
|
test_var session_group_size "2" -t "cov:"
|
||||||
|
|
||||||
|
# list-buffers -F formats each paste buffer (this fills in the paste-buffer
|
||||||
|
# format defaults).
|
||||||
|
if [ "$($TMUX list-buffers -F '#{buffer_name}=#{buffer_sample}')" != \
|
||||||
|
"buf0=somebuffer" ]; then
|
||||||
|
fail "Unexpected list-buffers format output."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Version reported by the variable matches tmux -V.
|
||||||
|
VER=$($TMUX -V | sed 's/^tmux //')
|
||||||
|
test_var version "$VER" -t "$TGT"
|
||||||
|
|
||||||
|
# Client variables from each kind of client.
|
||||||
|
test_var client_name "$CLIENT" -c "$CLIENT"
|
||||||
|
test_var client_control_mode "1" -c "$CLIENT"
|
||||||
|
test_var client_control_mode "0" -c "$RC"
|
||||||
|
test_var socket_path "$($TMUX display-message -p '#{socket_path}')" -c "$CLIENT"
|
||||||
|
# The real client has a terminal, so termcap/feature/environ queries work.
|
||||||
|
test_var "I/e:TERM" "$($TMUX display-message -c "$RC" -p '#{client_termname}')" \
|
||||||
|
-c "$RC"
|
||||||
|
# Termcap and feature queries against a real terminal return a boolean.
|
||||||
|
case "$($TMUX display-message -c "$RC" -p '#{I/c:colors}')" in
|
||||||
|
0|1) ;;
|
||||||
|
*) fail "Unexpected #{I/c:colors} for real client." ;;
|
||||||
|
esac
|
||||||
|
case "$($TMUX display-message -c "$RC" -p '#{I/f:256}')" in
|
||||||
|
0|1) ;;
|
||||||
|
*) fail "Unexpected #{I/f:256} for real client." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Time variables through the pretty and relative modifiers: start_time is the
|
||||||
|
# recent server start, exercising the "last 24 hours" and "just now" paths.
|
||||||
|
[ -n "$($TMUX display-message -p '#{t/p:start_time}')" ] ||
|
||||||
|
fail "Empty #{t/p:start_time}."
|
||||||
|
[ -n "$($TMUX display-message -p '#{t/r:start_time}')" ] ||
|
||||||
|
fail "Empty #{t/r:start_time}."
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
exit 0
|
||||||
176
regress/input-common.inc
Normal file
176
regress/input-common.inc
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
EXP=$(mktemp)
|
||||||
|
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||||
|
|
||||||
|
exit_status=0
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "FAIL: $1"
|
||||||
|
diff -u "$EXP" "$TMP"
|
||||||
|
exit_status=1
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pane()
|
||||||
|
{
|
||||||
|
start_pane_hlimit "$1" "$2" "$3" "$4" 0
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pane_history()
|
||||||
|
{
|
||||||
|
start_pane_hlimit "$1" "$2" "$3" "$4" 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pane_hlimit()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
sx=$2
|
||||||
|
sy=$3
|
||||||
|
seq=$4
|
||||||
|
hlimit=$5
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 0.1
|
||||||
|
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
|
||||||
|
$TMUX set-option -g history-limit "$hlimit" || exit 1
|
||||||
|
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" \
|
||||||
|
"printf '$seq'; sleep 2" || exit 1
|
||||||
|
$TMUX kill-session -t test-setup
|
||||||
|
sleep 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
start_cmd()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
sx=$2
|
||||||
|
sy=$3
|
||||||
|
cmd=$4
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 0.1
|
||||||
|
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" "$cmd" || exit 1
|
||||||
|
sleep 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_capture()
|
||||||
|
{
|
||||||
|
sed 's/[ ]*$//' |
|
||||||
|
awk '{ line[NR] = $0; if ($0 != "") last = NR }
|
||||||
|
END { for (i = 1; i <= last; i++) print line[i] }'
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_grid()
|
||||||
|
{
|
||||||
|
$TMUX capture-pane -pN -t "$1:" -S 0 -E - | normalize_capture
|
||||||
|
}
|
||||||
|
|
||||||
|
check_capture()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
capture_grid "$name" >"$TMP"
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_cursor()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
actual=$($TMUX display-message -p -t "$name:" '#{cursor_x},#{cursor_y}')
|
||||||
|
if [ "$actual" != "$expected" ]; then
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
printf "%s\n" "$actual" >"$TMP"
|
||||||
|
fail "$name cursor"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_flags()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
$TMUX capture-pane -pNF -t "$name:" -S 0 -E - |
|
||||||
|
normalize_capture |
|
||||||
|
awk '$0 != "-"' >"$TMP"
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name flags"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_joined()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
$TMUX capture-pane -pNJ -t "$name:" -S 0 -E - |
|
||||||
|
normalize_capture >"$TMP"
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name joined"
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_raw()
|
||||||
|
{
|
||||||
|
$TMUX capture-pane -pR -t "$1:"
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_raw_used()
|
||||||
|
{
|
||||||
|
capture_raw "$1" |
|
||||||
|
awk '/^(G| L)/ || /^ C/ && $3 !~ /^data=\(1,1, \)$/'
|
||||||
|
}
|
||||||
|
|
||||||
|
check_raw()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
capture_raw "$name" >"$TMP"
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name raw"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_raw_used()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
capture_raw_used "$name" >"$TMP"
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name raw used"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_raw_has()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
capture_raw "$name" >"$TMP"
|
||||||
|
for expected in "$@"; do
|
||||||
|
if ! grep -Fqx "$expected" "$TMP"; then
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
fail "$name raw missing"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
check_raw_matches()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
capture_raw "$name" >"$TMP"
|
||||||
|
for expected in "$@"; do
|
||||||
|
if ! grep -Eq "$expected" "$TMP"; then
|
||||||
|
printf "%s\n" "$expected" >"$EXP"
|
||||||
|
fail "$name raw missing"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
39
regress/input-cursor.sh
Normal file
39
regress/input-cursor.sh
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane cursor 10 3 'ABCDE\r\033[2Cxy\033[1D!\033[4GZ\n'
|
||||||
|
check_capture cursor 'ABxZE'
|
||||||
|
check_cursor cursor '0,1'
|
||||||
|
|
||||||
|
start_pane saverc 10 3 'abc\0337\033[2;5HXY\0338Z\n'
|
||||||
|
check_capture saverc 'abcZ
|
||||||
|
XY'
|
||||||
|
check_cursor saverc '0,1'
|
||||||
|
|
||||||
|
start_pane hvp 10 4 'A\033[3dB\033[5GC\033[2;2fD\n'
|
||||||
|
check_capture hvp 'A
|
||||||
|
D
|
||||||
|
B C'
|
||||||
|
check_cursor hvp '0,2'
|
||||||
|
|
||||||
|
start_pane cursorlines 8 4 'A\033[2BB\033[1FC\033[1AD\n'
|
||||||
|
check_capture cursorlines 'AD
|
||||||
|
C
|
||||||
|
B'
|
||||||
|
check_cursor cursorlines '0,1'
|
||||||
|
|
||||||
|
start_pane tabs 12 3 'a\tb\n'
|
||||||
|
check_capture tabs 'a b'
|
||||||
|
check_cursor tabs '0,1'
|
||||||
|
|
||||||
|
start_pane tabclear 12 3 '\033H\ta\033[3g\r\tb\n'
|
||||||
|
check_capture tabclear ' a b'
|
||||||
|
check_cursor tabclear '0,1'
|
||||||
|
|
||||||
|
start_pane cbt 16 3 '0123456789\r\033[10C\033[Zx\n'
|
||||||
|
check_capture cbt '01234567x9'
|
||||||
|
check_cursor cbt '0,1'
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
54
regress/input-edit.sh
Normal file
54
regress/input-edit.sh
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane dch 10 3 'abcdef\r\033[3C\033[2PXY\n'
|
||||||
|
check_capture dch 'abcXY'
|
||||||
|
|
||||||
|
start_pane ich 10 3 'abcdef\r\033[3C\033[2@XY\n'
|
||||||
|
check_capture ich 'abcXYdef'
|
||||||
|
|
||||||
|
start_pane erase 10 3 'abcdef\r\033[3C\033[KZ\n'
|
||||||
|
check_capture erase 'abcZ'
|
||||||
|
|
||||||
|
start_pane el1 10 3 'abcdef\r\033[3C\033[1KZ\n'
|
||||||
|
check_capture el1 ' Zef'
|
||||||
|
|
||||||
|
start_pane ech 10 3 'abcdef\r\033[3C\033[2XX\n'
|
||||||
|
check_capture ech 'abcX f'
|
||||||
|
|
||||||
|
start_pane ed 10 3 'one\ntwo\033[2;2H\033[JX\n'
|
||||||
|
check_capture ed 'one
|
||||||
|
tX'
|
||||||
|
|
||||||
|
start_pane ed1 10 3 'one\ntwo\033[2;2H\033[1JX\n'
|
||||||
|
check_capture ed1 '
|
||||||
|
Xo'
|
||||||
|
|
||||||
|
start_pane ed2 10 3 'one\ntwo\033[2JZ\n'
|
||||||
|
check_capture ed2 '
|
||||||
|
Z'
|
||||||
|
|
||||||
|
start_pane il 8 4 '111\n222\n333\033[2;1H\033[LAAA\n'
|
||||||
|
check_capture il '111
|
||||||
|
AAA
|
||||||
|
222
|
||||||
|
333'
|
||||||
|
|
||||||
|
start_pane dl 8 4 '111\n222\n333\033[2;1H\033[MZZZ\n'
|
||||||
|
check_capture dl '111
|
||||||
|
ZZZ'
|
||||||
|
|
||||||
|
start_pane irm 10 3 'abcdef\r\033[4h\033[3CXY\033[4lZ\n'
|
||||||
|
check_capture irm 'abcXYZef'
|
||||||
|
|
||||||
|
start_pane rep 10 3 'A\033[4bB\n'
|
||||||
|
check_capture rep 'AAAAAB'
|
||||||
|
|
||||||
|
start_pane decaln 6 3 '\033#8'
|
||||||
|
check_capture decaln 'EEEEEE
|
||||||
|
EEEEEE
|
||||||
|
EEEEEE'
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
42
regress/input-malformed.sh
Normal file
42
regress/input-malformed.sh
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_cmd csi-param-discard 8 3 \
|
||||||
|
"perl -e 'print qq{\e[}, q{1} x 80, qq{\030OK}'; sleep 2"
|
||||||
|
check_capture csi-param-discard 'OK'
|
||||||
|
|
||||||
|
start_cmd csi-interm-discard 8 3 \
|
||||||
|
"perl -e 'print qq{\e[ \030OK}'; sleep 2"
|
||||||
|
check_capture csi-interm-discard 'OK'
|
||||||
|
|
||||||
|
start_cmd osc-discard 8 3 \
|
||||||
|
"perl -e 'print qq{\e]2;}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
||||||
|
check_capture osc-discard 'OK'
|
||||||
|
|
||||||
|
start_cmd apc-discard 8 3 \
|
||||||
|
"perl -e 'print qq{\e_}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
||||||
|
check_capture apc-discard 'OK'
|
||||||
|
|
||||||
|
start_pane unknown-csi 8 3 '\033[?9999zOK'
|
||||||
|
check_capture unknown-csi 'OK'
|
||||||
|
|
||||||
|
start_pane unknown-osc 8 3 '\033]999;bad\aOK'
|
||||||
|
check_capture unknown-osc 'OK'
|
||||||
|
|
||||||
|
start_pane malformed-osc 8 3 '\033]8;id=a:id=b;http://bad\aX\033]8;id=no-separator\aY\033]9;4;5;200\a\033]9;4;z\a\033]10;notacolour\a\033]11;notacolour\a\033]12;notacolour\a\033]4;999;red\a\033]104;999\a\033]52bad\a\033]52;c;@@@\aOK'
|
||||||
|
check_capture malformed-osc 'XYOK'
|
||||||
|
check_raw_matches malformed-osc \
|
||||||
|
'C 0,0 data=\(1,1,X\).* link=NONE linkid=NONE' \
|
||||||
|
'C 0,1 data=\(1,1,Y\).* link=NONE linkid=NONE' \
|
||||||
|
'C 0,2 data=\(1,1,O\).* link=NONE linkid=NONE' \
|
||||||
|
'C 0,3 data=\(1,1,K\).* link=NONE linkid=NONE'
|
||||||
|
|
||||||
|
start_pane malformed-dcs 8 3 '\033P$qBAD\033\\OK'
|
||||||
|
check_capture malformed-dcs 'OK^[P0$r
|
||||||
|
^[\'
|
||||||
|
|
||||||
|
start_pane malformed-utf8 8 3 '\360\200\200\200A\355\240\200B'
|
||||||
|
check_capture malformed-utf8 '<27>A<EFBFBD>B'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
15
regress/input-modes.sh
Normal file
15
regress/input-modes.sh
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane alternate 10 3 'MAIN\033[?1049hALT\033[?1049lZ\n'
|
||||||
|
check_capture alternate 'MAINZ'
|
||||||
|
|
||||||
|
start_pane osc133 10 4 '\033]133;A\007prompt\n\033]133;C\007output\n'
|
||||||
|
check_capture osc133 'prompt
|
||||||
|
output'
|
||||||
|
check_flags osc133 'P prompt
|
||||||
|
O output'
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
37
regress/input-osc.sh
Normal file
37
regress/input-osc.sh
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane hyperlink 20 3 '\033]8;id=1;https://example.com\033\\link\033]8;;\033\\ plain\n'
|
||||||
|
check_capture hyperlink 'link plain'
|
||||||
|
check_flags hyperlink 'HX link plain'
|
||||||
|
$TMUX capture-pane -peH -t hyperlink: -S 0 -E - >/dev/null || exit 1
|
||||||
|
|
||||||
|
start_pane palette 20 3 '\033]4;1;rgb:11/22/33;2;red\007\033]104;1;2\007X\n'
|
||||||
|
check_capture palette 'X'
|
||||||
|
|
||||||
|
start_pane osc-colours 20 3 '\033]10;rgb:11/22/33\007\033]11;rgb:44/55/66\007\033]12;rgb:77/88/99\007\033]110\007\033]111\007\033]112\007X\n'
|
||||||
|
check_capture osc-colours 'X'
|
||||||
|
|
||||||
|
start_pane progress 20 3 '\033]9;4;1;25\007\033]9;4;0\007\033]9;4;5;200\007X\n'
|
||||||
|
check_capture progress 'X'
|
||||||
|
|
||||||
|
start_pane rename 20 3 '\033krenamed\033\\X\n'
|
||||||
|
check_capture rename 'X'
|
||||||
|
|
||||||
|
start_pane apc-title 20 3 '\033_test-title\033\\X\n'
|
||||||
|
check_capture apc-title 'X'
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 0.1
|
||||||
|
$TMUX new-session -d -x 20 -y 3 -s osc52 "sleep 2" || exit 1
|
||||||
|
$TMUX set-option -s set-clipboard on || exit 1
|
||||||
|
$TMUX respawn-pane -k -t osc52: \
|
||||||
|
"printf '\033]52;c;SGVsbG8=\007'; sleep 2" || exit 1
|
||||||
|
sleep 0.3
|
||||||
|
$TMUX save-buffer -b buffer0 - >"$TMP"
|
||||||
|
printf "Hello" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "osc52"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
84
regress/input-raw-controls.sh
Normal file
84
regress/input-raw-controls.sh
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane bs 8 3 'abc\bd'
|
||||||
|
check_capture bs 'abd'
|
||||||
|
check_raw_matches bs \
|
||||||
|
'C 0,0 data=\(1,1,a\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,b\) flags=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,d\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane nel 8 3 'A\033EB'
|
||||||
|
check_capture nel 'A
|
||||||
|
B'
|
||||||
|
check_raw_matches nel \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 1,0 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane tabstops 16 3 '\033H1\t2\033[3g\r\t3'
|
||||||
|
check_raw_matches tabstops \
|
||||||
|
'C 0,0 data=\(1,1,1\) flags=NONE\[0\]' \
|
||||||
|
'C 0,8 data=\(1,1,2\) flags=NONE\[0\]' \
|
||||||
|
'C 0,15 data=\(1,1,3\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane decaln 6 3 '\033#8'
|
||||||
|
check_raw_matches decaln \
|
||||||
|
'C 0,0 data=\(1,1,E\) flags=NONE\[0\]' \
|
||||||
|
'C 1,5 data=\(1,1,E\) flags=NONE\[0\]' \
|
||||||
|
'C 2,5 data=\(1,1,E\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane charset 8 3 '\033(0qxl\033(BZ'
|
||||||
|
check_raw_matches charset \
|
||||||
|
'C 0,0 data=\(1,1,q\) flags=NONE\[0\] attr=CHARSET\[[0-9a-f]+\]' \
|
||||||
|
'C 0,1 data=\(1,1,x\) flags=NONE\[0\] attr=CHARSET\[[0-9a-f]+\]' \
|
||||||
|
'C 0,2 data=\(1,1,l\) flags=NONE\[0\] attr=CHARSET\[[0-9a-f]+\]' \
|
||||||
|
'C 0,3 data=\(1,1,Z\) flags=NONE\[0\] attr=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane g1charset 8 3 '\033)0\016q\017Z'
|
||||||
|
check_raw_matches g1charset \
|
||||||
|
'C 0,0 data=\(1,1,q\) flags=NONE\[0\] attr=CHARSET\[[0-9a-f]+\]' \
|
||||||
|
'C 0,1 data=\(1,1,Z\) flags=NONE\[0\] attr=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane csisave 8 3 '\033[3;3HS\033[s\033[1;1HA\033[uR'
|
||||||
|
check_raw_matches csisave \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 2,2 data=\(1,1,S\) flags=NONE\[0\]' \
|
||||||
|
'C 2,3 data=\(1,1,R\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane alternate 8 3 'main\033[?1049halt\033[?1049lback'
|
||||||
|
check_capture alternate 'mainback'
|
||||||
|
check_raw_matches alternate \
|
||||||
|
'C 0,0 data=\(1,1,m\) flags=NONE\[0\]' \
|
||||||
|
'C 0,4 data=\(1,1,b\) flags=NONE\[0\]' \
|
||||||
|
'C 0,7 data=\(1,1,k\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane sync 8 3 '\033P=1signored\033\\A\033P=2s\033\\B'
|
||||||
|
check_raw_matches sync \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane private 8 3 '\033[?25lA\033[?25hB\033[?1000hC\033[?1000lD'
|
||||||
|
check_raw_matches private \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,B\) flags=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,C\) flags=NONE\[0\]' \
|
||||||
|
'C 0,3 data=\(1,1,D\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane ris 8 3 'A\033cB'
|
||||||
|
check_capture ris 'B'
|
||||||
|
check_raw_matches ris \
|
||||||
|
'C 0,0 data=\(1,1,B\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\]'
|
||||||
|
|
||||||
|
start_pane keypad 8 3 '\033=A\033>B'
|
||||||
|
check_raw_matches keypad \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane cursorstyle 8 3 '\033[5 qA\033[0 qB'
|
||||||
|
check_raw_matches cursorstyle \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
33
regress/input-raw-cursor.sh
Normal file
33
regress/input-raw-cursor.sh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane absolute 8 4 'A\033[3;5HB\033[2GC\033[2D!'
|
||||||
|
check_capture absolute 'A
|
||||||
|
|
||||||
|
!C B'
|
||||||
|
check_cursor absolute '1,2'
|
||||||
|
check_raw_matches absolute \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 2,4 data=\(1,1,B\) flags=NONE\[0\]' \
|
||||||
|
'C 2,0 data=\(1,1,!\) flags=NONE\[0\]' \
|
||||||
|
'C 2,1 data=\(1,1,C\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane savecursor 8 4 '\033[4;4HS\0337\033[1;1HA\0338R'
|
||||||
|
check_capture savecursor 'A
|
||||||
|
|
||||||
|
|
||||||
|
SR'
|
||||||
|
check_cursor savecursor '5,3'
|
||||||
|
check_raw_matches savecursor \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 3,3 data=\(1,1,S\) flags=NONE\[0\]' \
|
||||||
|
'C 3,4 data=\(1,1,R\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane origin 8 5 '\033[2;4r\033[?6h\033[1;1HO\033[3;1HP\033[?6lQ'
|
||||||
|
check_raw_matches origin \
|
||||||
|
'C 1,0 data=\(1,1,O\) flags=NONE\[0\]' \
|
||||||
|
'C 3,0 data=\(1,1,P\) flags=NONE\[0\]' \
|
||||||
|
'C 0,0 data=\(1,1,Q\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
47
regress/input-raw-edit.sh
Normal file
47
regress/input-raw-edit.sh
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane erasechars 8 3 'ABCDEFGH\r\033[3C\033[2X'
|
||||||
|
check_capture erasechars 'ABC FGH'
|
||||||
|
check_raw_matches erasechars \
|
||||||
|
'C 0,3 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,4 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,5 data=\(1,1,F\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane deletechars 8 3 'ABCDEFGH\r\033[3C\033[3P'
|
||||||
|
check_capture deletechars 'ABCGH'
|
||||||
|
check_raw_matches deletechars \
|
||||||
|
'C 0,3 data=\(1,1,G\) flags=NONE\[0\]' \
|
||||||
|
'C 0,4 data=\(1,1,H\) flags=NONE\[0\]' \
|
||||||
|
'C 0,5 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane insertchars 8 3 'ABCDEF\r\033[3C\033[2@xy'
|
||||||
|
check_capture insertchars 'ABCxyDEF'
|
||||||
|
check_raw_matches insertchars \
|
||||||
|
'C 0,3 data=\(1,1,x\) flags=NONE\[0\]' \
|
||||||
|
'C 0,4 data=\(1,1,y\) flags=NONE\[0\]' \
|
||||||
|
'C 0,5 data=\(1,1,D\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane eraseline 8 3 'ABCDEFGH\r\033[4C\033[K'
|
||||||
|
check_capture eraseline 'ABCD'
|
||||||
|
check_raw_matches eraseline \
|
||||||
|
'C 0,4 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,7 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane erasescreen 8 3 '1111111\033[2;1H2222222\033[H\033[JZ'
|
||||||
|
check_capture erasescreen 'Z'
|
||||||
|
check_raw_matches erasescreen \
|
||||||
|
'^G 8x3 \(0/0\)$' \
|
||||||
|
'C [0-9]+,0 data=\(1,1,Z\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,1 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane tabs 12 3 'A\tB\033[2g\r\033[IC'
|
||||||
|
check_raw_matches tabs \
|
||||||
|
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,1 data=\(7,7, \) flags=TAB\[[0-9a-f]+\]' \
|
||||||
|
'C 0,2 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\]' \
|
||||||
|
'C 0,8 data=\(1,1,B\) flags=NONE\[0\]' \
|
||||||
|
'C 0,0 data=\(1,1,C\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
23
regress/input-raw-history.sh
Normal file
23
regress/input-raw-history.sh
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane_hlimit trim 6 3 'one\ntwo\nthree\nfour\nfive\nsix' 2
|
||||||
|
check_raw_matches trim \
|
||||||
|
'^G 6x3 \(2/2\)$' \
|
||||||
|
'L 0 \(-\) flags=NONE\[0\]' \
|
||||||
|
'L 1 \(-\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
$TMUX clear-history -t trim:
|
||||||
|
check_raw_matches trim \
|
||||||
|
'^G 6x3 \(0/2\)$' \
|
||||||
|
'C 0,0 data=\(1,1,f\) flags=NONE\[0\]' \
|
||||||
|
'C 2,0 data=\(1,1,s\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane_hlimit edhistory 6 3 'one\ntwo\nthree\033[H\033[JZ' 5
|
||||||
|
check_raw_matches edhistory \
|
||||||
|
'^G 6x3 \([1-9][0-9]*/5\)$' \
|
||||||
|
'L [0-9]+ \(-\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,0 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
21
regress/input-raw-reflow.sh
Normal file
21
regress/input-raw-reflow.sh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane_history reflow 8 4 'abcdefgh\nijklmnop\nqrstuvwx\nyz'
|
||||||
|
$TMUX resize-window -t reflow: -x 4 -y 4
|
||||||
|
sleep 0.2
|
||||||
|
check_raw_matches reflow \
|
||||||
|
'^G 4x4 \([0-9]+/2000\)$' \
|
||||||
|
'L [0-9]+ \([0-9-]+\) flags=WRAPPED\[[0-9a-f]+\]' \
|
||||||
|
'C [0-9]+,0 data=\(1,1,a\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,3 data=\(1,1,d\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
$TMUX resize-window -t reflow: -x 12 -y 4
|
||||||
|
sleep 0.2
|
||||||
|
check_raw_matches reflow \
|
||||||
|
'^G 12x4 \([0-9]+/2000\)$' \
|
||||||
|
'C [0-9]+,0 data=\(1,1,a\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,7 data=\(1,1,h\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
39
regress/input-raw-scroll.sh
Normal file
39
regress/input-raw-scroll.sh
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane_history history 6 3 'one\ntwo\nthree\nfour\nfive'
|
||||||
|
check_raw_matches history \
|
||||||
|
'^G 6x3 \([1-9][0-9]*/2000\)$' \
|
||||||
|
'L [0-9]+ \(-\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,0 data=\(1,1,o\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane_history index 6 4 'A\nB\nC\033[2;3r\033[2;1HX\033D\033DY'
|
||||||
|
check_raw_matches index \
|
||||||
|
'C [0-9]+,0 data=\(1,1,X\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C [0-9]+,1 data=\(1,1,Y\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane reverse 6 4 'A\nB\nC\033[2;3r\033[2;1H\033MY'
|
||||||
|
check_raw_matches reverse \
|
||||||
|
'C 1,0 data=\(1,1,Y\) flags=NONE\[0\]' \
|
||||||
|
'C 2,0 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane insertline 6 4 'A\nB\nC\033[2;3r\033[2;1H\033[LY'
|
||||||
|
check_raw_matches insertline \
|
||||||
|
'C 1,0 data=\(1,1,Y\) flags=NONE\[0\]' \
|
||||||
|
'C 2,0 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane deleteline 6 4 'A\nB\nC\033[2;3r\033[2;1H\033[MY'
|
||||||
|
check_raw_matches deleteline \
|
||||||
|
'C 1,0 data=\(1,1,Y\) flags=NONE\[0\]' \
|
||||||
|
'C 2,0 data=\(1,1, \) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane region-edge 6 4 'top\033[2;3rmid\033[2;1H\033D\033Mbot'
|
||||||
|
check_raw_matches region-edge \
|
||||||
|
'C 0,0 data=\(1,1,m\) flags=NONE\[0\]' \
|
||||||
|
'C 1,0 data=\(1,1,b\) flags=NONE\[0\]' \
|
||||||
|
'C 2,0 data=\(1,1, \) flags=NONE\[0\]' \
|
||||||
|
'C 3,0 data=\(1,1, \) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
39
regress/input-raw-sgr.sh
Normal file
39
regress/input-raw-sgr.sh
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane attrs 16 3 '\033[1mB\033[2mD\033[3mI\033[4mU\033[5mK\033[7mR\033[8mH\033[9mS\033[53mO'
|
||||||
|
check_raw_matches attrs \
|
||||||
|
'C 0,0 data=\(1,1,B\) flags=NONE\[0\] attr=BRIGHT\[[0-9a-f]+\]' \
|
||||||
|
'C 0,1 data=\(1,1,D\) flags=NONE\[0\] attr=BRIGHT,DIM\[[0-9a-f]+\]' \
|
||||||
|
'C 0,2 data=\(1,1,I\) flags=NONE\[0\] attr=BRIGHT,DIM,ITALICS\[[0-9a-f]+\]' \
|
||||||
|
'C 0,3 data=\(1,1,U\) flags=NONE\[0\] attr=BRIGHT,DIM,UNDERSCORE,ITALICS\[[0-9a-f]+\]' \
|
||||||
|
'C 0,5 data=\(1,1,R\) flags=NONE\[0\] attr=BRIGHT,DIM,UNDERSCORE,BLINK,REVERSE,ITALICS\[[0-9a-f]+\]' \
|
||||||
|
'C 0,7 data=\(1,1,S\) flags=NONE\[0\] attr=BRIGHT,DIM,UNDERSCORE,BLINK,REVERSE,HIDDEN,ITALICS,STRIKETHROUGH\[[0-9a-f]+\]' \
|
||||||
|
'C 0,8 data=\(1,1,O\) flags=NONE\[0\] attr=BRIGHT,DIM,UNDERSCORE,BLINK,REVERSE,HIDDEN,ITALICS,STRIKETHROUGH,OVERLINE\[[0-9a-f]+\]'
|
||||||
|
|
||||||
|
start_pane colours 12 3 '\033[38;5;196;48;5;17mX\033[58;5;45mY'
|
||||||
|
check_raw_matches colours \
|
||||||
|
'C 0,0 data=\(1,1,X\) flags=FG256,BG256\[[0-9a-f]+\] attr=NONE\[0\] fg=colour196\[10000c4\] bg=colour17\[1000011\]' \
|
||||||
|
'C 0,1 data=\(1,1,Y\) flags=FG256,BG256\[[0-9a-f]+\] attr=NONE\[0\] fg=colour196\[10000c4\] bg=colour17\[1000011\] us=colour45\[100002d\]'
|
||||||
|
|
||||||
|
start_pane bce 8 3 '\033[44mA\033[K'
|
||||||
|
check_raw_matches bce \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\] attr=NONE\[0\].* bg=blue\[4\]' \
|
||||||
|
'C 0,1 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\].* bg=blue\[4\]' \
|
||||||
|
'C 0,7 data=\(1,1, \) flags=CLEARED\[[0-9a-f]+\] attr=NONE\[0\].* bg=blue\[4\]'
|
||||||
|
|
||||||
|
start_pane underlines 12 3 '\033[4:2m2\033[4:3m3\033[4:4m4\033[4:5m5'
|
||||||
|
check_raw_matches underlines \
|
||||||
|
'C 0,0 data=\(1,1,2\) flags=NONE\[0\] attr=UNDERSCORE_2\[[0-9a-f]+\]' \
|
||||||
|
'C 0,1 data=\(1,1,3\) flags=NONE\[0\] attr=UNDERSCORE_3\[[0-9a-f]+\]' \
|
||||||
|
'C 0,2 data=\(1,1,4\) flags=NONE\[0\] attr=UNDERSCORE_4\[[0-9a-f]+\]' \
|
||||||
|
'C 0,3 data=\(1,1,5\) flags=NONE\[0\] attr=UNDERSCORE_5\[[0-9a-f]+\]'
|
||||||
|
|
||||||
|
start_pane hyperlink 12 3 '\033]8;id=id1;https://example.com/a\033\\A\033]8;;\033\\B'
|
||||||
|
check_raw_matches hyperlink \
|
||||||
|
'L 0 \(0\) flags=EXTENDED,HYPERLINK\[[0-9a-f]+\]' \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\].* link=https://example.com/a linkid=id1' \
|
||||||
|
'C 0,1 data=\(1,1,B\) flags=NONE\[0\].* link=NONE linkid=NONE'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
82
regress/input-raw-unicode.sh
Normal file
82
regress/input-raw-unicode.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane wide 8 3 'A\343\201\202B'
|
||||||
|
check_capture wide 'AあB'
|
||||||
|
check_raw_matches wide \
|
||||||
|
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,1 data=\(2,3,あ\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,3 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane combining 8 3 'e\314\201x'
|
||||||
|
check_raw_matches combining \
|
||||||
|
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,0 data=\(1,[0-9]+,.*\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,x\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane emoji 10 3 '\360\237\230\200Z'
|
||||||
|
check_raw_matches emoji \
|
||||||
|
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,0 data=\(2,4,😀\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane flag 10 3 '\360\237\207\254\360\237\207\247!'
|
||||||
|
check_raw_matches flag \
|
||||||
|
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,0 data=\(2,8,🇬🇧\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,!\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane variation 10 3 '*\357\270\217!'
|
||||||
|
check_raw_matches variation \
|
||||||
|
'L 0 \(0\) flags=EXTENDED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,0 data=\(2,[0-9]+,.*\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,!\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane invalid 10 3 '\377A'
|
||||||
|
check_raw_matches invalid \
|
||||||
|
'C 0,0 data=\(1,3,.*\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,A\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane trunc2 10 3 '\303A'
|
||||||
|
check_raw_matches trunc2 \
|
||||||
|
'C 0,0 data=\(1,3,.*\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,A\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane trunc3 10 3 '\342\202A'
|
||||||
|
check_raw_matches trunc3 \
|
||||||
|
'C 0,0 data=\(1,3,.*\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,A\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane overwrite-wide-left 10 3 'A\343\201\202B\r\033[1CX'
|
||||||
|
check_raw_matches overwrite-wide-left \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1,X\) flags=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1, \) flags=NONE\[0\]' \
|
||||||
|
'C 0,3 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane overwrite-wide-pad 10 3 'A\343\201\202B\r\033[2CX'
|
||||||
|
check_raw_matches overwrite-wide-pad \
|
||||||
|
'C 0,0 data=\(1,1,A\) flags=NONE\[0\]' \
|
||||||
|
'C 0,1 data=\(1,1, \) flags=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,X\) flags=NONE\[0\]' \
|
||||||
|
'C 0,3 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane overwrite-wide-with-wide 10 3 'A\343\201\202B\r\033[1C\347\225\214'
|
||||||
|
check_raw_matches overwrite-wide-with-wide \
|
||||||
|
'C 0,1 data=\(2,3,界\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 0,2 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 0,3 data=\(1,1,B\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane wide-right-edge 4 3 'ABC\343\201\202Z'
|
||||||
|
check_raw_matches wide-right-edge \
|
||||||
|
'L 0 \(0\) flags=WRAPPED\[[0-9a-f]+\]' \
|
||||||
|
'C 1,0 data=\(2,3,あ\) flags=NONE\[0\] attr=NONE\[0\]' \
|
||||||
|
'C 1,1 data=\(1,1,!\) flags=PADDING\[[0-9a-f]+\] attr=NONE\[0\]' \
|
||||||
|
'C 1,2 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
28
regress/input-raw-wrap.sh
Normal file
28
regress/input-raw-wrap.sh
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane wrap 5 3 'ABCDEZ'
|
||||||
|
check_capture wrap 'ABCDE
|
||||||
|
Z'
|
||||||
|
check_raw_matches wrap \
|
||||||
|
'^G 5x3 \(0/0\)$' \
|
||||||
|
'L 0 \(0\) flags=WRAPPED\[[0-9a-f]+\]' \
|
||||||
|
'C 0,4 data=\(1,1,E\) flags=NONE\[0\]' \
|
||||||
|
'C 1,0 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane nowrap 5 3 '\033[?7lABCDEZ'
|
||||||
|
check_capture nowrap 'ABCDZ'
|
||||||
|
check_raw_matches nowrap \
|
||||||
|
'^G 5x3 \(0/0\)$' \
|
||||||
|
'L 0 \(0\) flags=NONE\[0\]' \
|
||||||
|
'C 0,4 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
start_pane pending 5 3 'ABCD\r\033[4CZ'
|
||||||
|
check_capture pending 'ABCDZ'
|
||||||
|
check_cursor pending '5,0'
|
||||||
|
check_raw_matches pending \
|
||||||
|
'L 0 \(0\) flags=NONE\[0\]' \
|
||||||
|
'C 0,4 data=\(1,1,Z\) flags=NONE\[0\]'
|
||||||
|
|
||||||
|
exit $exit_status
|
||||||
94
regress/input-replies.sh
Normal file
94
regress/input-replies.sh
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 0.1
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
EXP=$(mktemp)
|
||||||
|
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||||
|
|
||||||
|
$TMUX new-session -d -x 80 -y 24 -s replies \; \
|
||||||
|
set-window-option -t replies:0 remain-on-exit on || exit 1
|
||||||
|
$TMUX set-option -s set-clipboard on || exit 1
|
||||||
|
$TMUX set-option -s get-clipboard buffer || exit 1
|
||||||
|
printf Hello | $TMUX load-buffer -
|
||||||
|
sleep 0.3
|
||||||
|
|
||||||
|
exit_status=0
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "FAIL: $1"
|
||||||
|
diff -u "$EXP" "$TMP"
|
||||||
|
exit_status=1
|
||||||
|
}
|
||||||
|
|
||||||
|
query()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
seq=$3
|
||||||
|
count=$4
|
||||||
|
setup=$5
|
||||||
|
|
||||||
|
$TMUX respawn-window -k -t replies:0 \
|
||||||
|
"stty raw -echo min 1 time 20; printf '$setup'; printf '$seq'; dd bs=1 count=$count 2>/dev/null | cat -v >$TMP"
|
||||||
|
sleep 0.5
|
||||||
|
printf "%s" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
query_timeout()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
seq=$3
|
||||||
|
setup=$4
|
||||||
|
|
||||||
|
$TMUX respawn-window -k -t replies:0 \
|
||||||
|
"stty raw -echo min 0 time 5; printf '$setup'; printf '$seq'; sleep 0.1; dd bs=1 count=128 2>/dev/null | cat -v >$TMP"
|
||||||
|
sleep 0.7
|
||||||
|
printf "%s" "$expected" >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
query "dsr-ok" '^[[0n' '\033[5n' 4 ''
|
||||||
|
query "dsr-cursor" '^[[1;1R' '\033[6n' 6 ''
|
||||||
|
query "da-primary" '^[[?1;2c' '\033[c' 7 ''
|
||||||
|
query "da-secondary" '^[[>84;0;0c' '\033[>c' 10 ''
|
||||||
|
query "decrqm-irm-reset" '^[[4;2$y' '\033[4$p' 8 ''
|
||||||
|
query "decrqm-irm-set" '^[[4;1$y' '\033[4$p' 8 '\033[4h'
|
||||||
|
query "decrqm-cursor-keys-reset" '^[[?1;2$y' '\033[?1$p' 9 ''
|
||||||
|
query "decrqm-cursor-keys-set" '^[[?1;1$y' '\033[?1$p' 9 '\033[?1h'
|
||||||
|
query "decrqm-columns" '^[[?3;4$y' '\033[?3$p' 9 ''
|
||||||
|
query "decrqm-origin-reset" '^[[?6;2$y' '\033[?6$p' 9 ''
|
||||||
|
query "decrqm-origin-set" '^[[?6;1$y' '\033[?6$p' 9 '\033[?6h'
|
||||||
|
query "decrqm-wrap-set" '^[[?7;1$y' '\033[?7$p' 9 ''
|
||||||
|
query "decrqm-wrap-reset" '^[[?7;2$y' '\033[?7$p' 9 '\033[?7l'
|
||||||
|
query "decrqm-cursor-visible-set" '^[[?25;1$y' '\033[?25$p' 10 ''
|
||||||
|
query "decrqm-cursor-visible-reset" '^[[?25;2$y' '\033[?25$p' 10 '\033[?25l'
|
||||||
|
query "decrqm-mouse-standard-set" '^[[?1000;1$y' '\033[?1000$p' 12 '\033[?1000h'
|
||||||
|
query "decrqm-mouse-button-set" '^[[?1002;1$y' '\033[?1002$p' 12 '\033[?1002h'
|
||||||
|
query "decrqm-mouse-all-set" '^[[?1003;1$y' '\033[?1003$p' 12 '\033[?1003h'
|
||||||
|
query "decrqm-focus-set" '^[[?1004;1$y' '\033[?1004$p' 12 '\033[?1004h'
|
||||||
|
query "decrqm-mouse-utf8-set" '^[[?1005;1$y' '\033[?1005$p' 12 '\033[?1005h'
|
||||||
|
query "decrqm-mouse-sgr-set" '^[[?1006;1$y' '\033[?1006$p' 12 '\033[?1006h'
|
||||||
|
query "decrqm-bracket-paste-set" '^[[?2004;1$y' '\033[?2004$p' 12 '\033[?2004h'
|
||||||
|
query "decrqm-theme-updates-set" '^[[?2031;1$y' '\033[?2031$p' 12 '\033[?2031h'
|
||||||
|
query "decrqss-cursor-style" '^[P1$r q0 q' '\033P$q q\033\\' 10 ''
|
||||||
|
|
||||||
|
query_timeout "osc-10-query" '^[]10;rgb:ffff/0000/0000^G' '\033]10;?\007' '\033]10;red\007'
|
||||||
|
query_timeout "osc-11-query" '^[]11;rgb:0000/0000/ffff^G' '\033]11;?\007' '\033]11;blue\007'
|
||||||
|
query_timeout "osc-12-query" '^[]12;rgb:0000/ffff/0000^G' '\033]12;?\007' '\033]12;green\007'
|
||||||
|
query_timeout "osc-4-query" '^[]4;1;rgb:ffff/0000/0000^G' '\033]4;1;?\007' '\033]4;1;red\007'
|
||||||
|
query_timeout "osc-104-reset-query" '' '\033]4;1;?\007' '\033]4;1;red\007\033]104;1\007'
|
||||||
|
query_timeout "osc-52-query" '^[]52;c;SGVsbG8=^G' '\033]52;c;?\007' ''
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
117
regress/input-requests.sh
Normal file
117
regress/input-requests.sh
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
python3 - "$TEST_TMUX" <<'PY'
|
||||||
|
import os
|
||||||
|
import select
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
tmux = sys.argv[1]
|
||||||
|
server = [tmux, "-Ltest", "-f/dev/null"]
|
||||||
|
|
||||||
|
def run(*args, check=True):
|
||||||
|
return subprocess.run(server + list(args), check=check,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
def attach():
|
||||||
|
pid, fd = os.forkpty()
|
||||||
|
if pid == 0:
|
||||||
|
os.environ["TERM"] = "xterm-256color"
|
||||||
|
os.execl(tmux, tmux, "-Ltest", "-f/dev/null", "attach-session",
|
||||||
|
"-t", "requests")
|
||||||
|
os.set_blocking(fd, False)
|
||||||
|
return pid, fd
|
||||||
|
|
||||||
|
def read_until(fd, needle, timeout=5):
|
||||||
|
end = time.time() + timeout
|
||||||
|
data = b""
|
||||||
|
while time.time() < end:
|
||||||
|
r, _, _ = select.select([fd], [], [], 0.05)
|
||||||
|
if fd in r:
|
||||||
|
try:
|
||||||
|
chunk = os.read(fd, 4096)
|
||||||
|
except BlockingIOError:
|
||||||
|
chunk = b""
|
||||||
|
if chunk == b"":
|
||||||
|
continue
|
||||||
|
data += chunk
|
||||||
|
if needle in data:
|
||||||
|
return data
|
||||||
|
raise RuntimeError("did not see terminal request %r in %r" %
|
||||||
|
(needle, data))
|
||||||
|
|
||||||
|
def wait_file(path, timeout=5):
|
||||||
|
end = time.time() + timeout
|
||||||
|
while time.time() < end:
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
time.sleep(0.05)
|
||||||
|
return b""
|
||||||
|
|
||||||
|
def respawn(command):
|
||||||
|
run("respawn-window", "-k", "-t", "requests:0", command)
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
def cleanup(pid=None):
|
||||||
|
if pid is not None:
|
||||||
|
try:
|
||||||
|
os.kill(pid, signal.SIGHUP)
|
||||||
|
except ProcessLookupError:
|
||||||
|
pass
|
||||||
|
run("kill-server", check=False)
|
||||||
|
|
||||||
|
run("kill-server", check=False)
|
||||||
|
run("new-session", "-d", "-x", "80", "-y", "24", "-s", "requests",
|
||||||
|
"sleep 60")
|
||||||
|
|
||||||
|
pid, fd = attach()
|
||||||
|
try:
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
palette_out = f.name
|
||||||
|
respawn("stty raw -echo min 1 time 50; "
|
||||||
|
"printf '\\033]4;99;?\\033\\\\'; "
|
||||||
|
"dd bs=1 count=64 2>/dev/null | cat -v >%s; sleep 1" %
|
||||||
|
palette_out)
|
||||||
|
read_until(fd, b"\033]4;99;?\033\\")
|
||||||
|
os.write(fd, b"\033]4;99;rgb:0101/0202/0303\033\\")
|
||||||
|
got = wait_file(palette_out)
|
||||||
|
expected = b"^[]4;99;rgb:0101/0202/0303^[\\"
|
||||||
|
if got != expected:
|
||||||
|
raise AssertionError("palette reply: expected %r got %r" %
|
||||||
|
(expected, got))
|
||||||
|
|
||||||
|
run("set-option", "-s", "set-clipboard", "on")
|
||||||
|
run("set-option", "-s", "get-clipboard", "request")
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
clip_out = f.name
|
||||||
|
respawn("stty raw -echo min 1 time 50; "
|
||||||
|
"printf '\\033]52;c;?\\033\\\\'; "
|
||||||
|
"dd bs=1 count=64 2>/dev/null | cat -v >%s; sleep 1" %
|
||||||
|
clip_out)
|
||||||
|
data = read_until(fd, b"]52;")
|
||||||
|
if b"?" not in data:
|
||||||
|
raise RuntimeError("clipboard request missing query in %r" % data)
|
||||||
|
os.write(fd, b"\033]52;c;UmVxdWVzdA==\033\\")
|
||||||
|
got = wait_file(clip_out)
|
||||||
|
expected = b"^[]52;c;UmVxdWVzdA==^[\\"
|
||||||
|
if got != expected:
|
||||||
|
raise AssertionError("clipboard reply: expected %r got %r" %
|
||||||
|
(expected, got))
|
||||||
|
finally:
|
||||||
|
cleanup(pid)
|
||||||
|
PY
|
||||||
73
regress/input-scroll.sh
Normal file
73
regress/input-scroll.sh
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane wrap 5 3 'abcdeF'
|
||||||
|
check_capture wrap 'abcde
|
||||||
|
F'
|
||||||
|
check_cursor wrap '1,1'
|
||||||
|
check_flags wrap 'W abcde
|
||||||
|
- F'
|
||||||
|
check_joined wrap 'abcdeF'
|
||||||
|
|
||||||
|
start_pane wraplast 5 3 'abcd\033[5GZQ'
|
||||||
|
check_capture wraplast 'abcdZ
|
||||||
|
Q'
|
||||||
|
check_cursor wraplast '1,1'
|
||||||
|
|
||||||
|
start_pane nowrap 5 3 '\033[?7labcdeF'
|
||||||
|
check_capture nowrap 'abcdF'
|
||||||
|
check_cursor nowrap '4,0'
|
||||||
|
|
||||||
|
start_pane origin 6 4 '111111\n222222\n333333\n444444\033[2;3r\033[?6h\033[1;1HAA\033[?6l\033[r'
|
||||||
|
check_capture origin '111111
|
||||||
|
AA2222
|
||||||
|
333333
|
||||||
|
444444'
|
||||||
|
|
||||||
|
start_pane scrollup 5 4 '11111\n22222\n33333\n44444\033[2;3r\033[3;1HAAAAA\nBBBBB\033[r'
|
||||||
|
check_capture scrollup '11111
|
||||||
|
AAAAA
|
||||||
|
BBBBB
|
||||||
|
44444'
|
||||||
|
|
||||||
|
start_pane scrolldown 5 4 '11111\n22222\n33333\n44444\033[2;3r\033[2;1H\033[TZZZZZ\033[r'
|
||||||
|
check_capture scrolldown '11111
|
||||||
|
ZZZZZ
|
||||||
|
22222
|
||||||
|
44444'
|
||||||
|
|
||||||
|
start_pane ri 5 4 '11111\n22222\n33333\n44444\033[2;3r\033[2;1H\033MZZZZZ\033[r'
|
||||||
|
check_capture ri '11111
|
||||||
|
ZZZZZ
|
||||||
|
22222
|
||||||
|
44444'
|
||||||
|
|
||||||
|
start_pane nel 5 3 'AA\033EBC\n'
|
||||||
|
check_capture nel 'AA
|
||||||
|
BC'
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 0.1
|
||||||
|
$TMUX new-session -d -x 5 -y 3 -s history \; \
|
||||||
|
set-option -g history-limit 3 \; \
|
||||||
|
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; sleep 2" || exit 1
|
||||||
|
sleep 0.3
|
||||||
|
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
|
||||||
|
printf "%s\n" '01
|
||||||
|
02
|
||||||
|
03
|
||||||
|
04
|
||||||
|
05
|
||||||
|
06' >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "history limit"
|
||||||
|
|
||||||
|
$TMUX clear-history -t history:
|
||||||
|
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
|
||||||
|
printf "%s\n" '04
|
||||||
|
05
|
||||||
|
06' >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "clear-history"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
26
regress/input-sgr.sh
Normal file
26
regress/input-sgr.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane sgr-basic 20 3 '\033[1;2;3;4;5;7;8;9mA\033[22;23;24;25;27;28;29mB\n'
|
||||||
|
check_capture sgr-basic 'AB'
|
||||||
|
$TMUX capture-pane -peN -t sgr-basic: -S 0 -E - >/dev/null || exit 1
|
||||||
|
|
||||||
|
start_pane sgr-colour 20 3 '\033[31;42mA\033[38;5;196;48;5;22mB\033[38;2;1;2;3;48;2;4;5;6mC\033[39;49mD\n'
|
||||||
|
check_capture sgr-colour 'ABCD'
|
||||||
|
$TMUX capture-pane -peN -t sgr-colour: -S 0 -E - >/dev/null || exit 1
|
||||||
|
|
||||||
|
start_pane sgr-underline 20 3 '\033[4:1mA\033[4:2mB\033[4:3mC\033[4:4mD\033[4:5mE\033[4:0mF\n'
|
||||||
|
check_capture sgr-underline 'ABCDEF'
|
||||||
|
$TMUX capture-pane -peN -t sgr-underline: -S 0 -E - >/dev/null || exit 1
|
||||||
|
|
||||||
|
start_pane sgr-uscolour 20 3 '\033[58;5;45;4mA\033[58:2::10:20:30mB\033[59mC\n'
|
||||||
|
check_capture sgr-uscolour 'ABC'
|
||||||
|
$TMUX capture-pane -peN -t sgr-uscolour: -S 0 -E - >/dev/null || exit 1
|
||||||
|
|
||||||
|
start_pane sgr-reset 20 3 '\033[90;100mA\033[0mB\033[91;101mC\033[39;49mD\n'
|
||||||
|
check_capture sgr-reset 'ABCD'
|
||||||
|
$TMUX capture-pane -peN -t sgr-reset: -S 0 -E - >/dev/null || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
45
regress/input-unicode.sh
Normal file
45
regress/input-unicode.sh
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./input-common.inc
|
||||||
|
|
||||||
|
start_pane wide 10 3 '\343\201\202B\rX\n'
|
||||||
|
check_capture wide 'X B'
|
||||||
|
check_flags wide 'X X B'
|
||||||
|
|
||||||
|
start_pane widepad 10 3 'A\343\201\202B\r\033[2CX\n'
|
||||||
|
check_capture widepad 'A XB'
|
||||||
|
check_flags widepad 'X A XB'
|
||||||
|
|
||||||
|
start_pane wideedge 5 3 'abc\343\201\202Z\n'
|
||||||
|
check_capture wideedge 'abcあ
|
||||||
|
Z'
|
||||||
|
check_cursor wideedge '0,2'
|
||||||
|
check_joined wideedge 'abcあZ'
|
||||||
|
|
||||||
|
start_pane wideeol 5 3 'abcd\343\201\202Z\n'
|
||||||
|
check_capture wideeol 'abcd
|
||||||
|
あZ'
|
||||||
|
check_cursor wideeol '0,2'
|
||||||
|
|
||||||
|
start_pane combine 10 3 'e\314\201\n'
|
||||||
|
check_capture combine 'é'
|
||||||
|
check_cursor combine '0,1'
|
||||||
|
|
||||||
|
start_pane combinewide 10 3 '\343\201\202\314\201X\n'
|
||||||
|
check_capture combinewide 'あ́X'
|
||||||
|
check_cursor combinewide '0,1'
|
||||||
|
|
||||||
|
start_pane variation 10 3 '\342\234\224\357\270\217X\n'
|
||||||
|
check_capture variation '✔️X'
|
||||||
|
check_cursor variation '0,1'
|
||||||
|
|
||||||
|
start_pane flag 10 3 '\360\237\207\254\360\237\207\247X\n'
|
||||||
|
check_capture flag '🇬🇧X'
|
||||||
|
check_cursor flag '0,1'
|
||||||
|
|
||||||
|
start_pane combining-left 10 3 '\314\201A\n'
|
||||||
|
check_capture combining-left 'A'
|
||||||
|
check_cursor combining-left '0,1'
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit $exit_status
|
||||||
161
regress/options-array.sh
Normal file
161
regress/options-array.sh
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of array options in the options engine (options_array_* in options.c
|
||||||
|
# and the array handling in cmd-set-option.c / cmd-show-options.c).
|
||||||
|
#
|
||||||
|
# Array options are indexed by integer. This exercises: setting a whole array
|
||||||
|
# from a separator-delimited string; per-index set with option[N]; -a append
|
||||||
|
# (which lands at the next free index); show ordering by ascending index and
|
||||||
|
# preservation of gaps; per-index unset with -u; show -v of a single index and
|
||||||
|
# of a missing index; and per-option separators (user-keys splits only on
|
||||||
|
# comma, update-environment on space or comma).
|
||||||
|
#
|
||||||
|
# update-environment (session), status-format (session), user-keys (server)
|
||||||
|
# and command-alias (server) are used as representative array options.
|
||||||
|
#
|
||||||
|
# options-scope.sh covers scoping/inheritance and options-values.sh covers
|
||||||
|
# value validation.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
check_value()
|
||||||
|
{
|
||||||
|
out=$($TMUX show $1 2>&1)
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "show $1 failed."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# check_array $args $expected
|
||||||
|
#
|
||||||
|
# Compare the full (multi-line) show output for an array option with a
|
||||||
|
# newline-separated $expected string.
|
||||||
|
check_array()
|
||||||
|
{
|
||||||
|
out=$($TMUX show $1 2>&1)
|
||||||
|
if [ "$out" != "$(printf '%s' "$2")" ]; then
|
||||||
|
echo "show $1 (array) failed."
|
||||||
|
echo "Expected:"; printf '%s\n' "$2"
|
||||||
|
echo "But got:"; printf '%s\n' "$out"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ok()
|
||||||
|
{
|
||||||
|
if ! $TMUX "$@"; then
|
||||||
|
echo "Command failed (expected success): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_fail()
|
||||||
|
{
|
||||||
|
exp="$1"
|
||||||
|
shift
|
||||||
|
out=$($TMUX "$@" 2>&1)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Command succeeded (expected failure): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Wrong error for: $*"
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server died: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# --- whole-array assignment splits on the separator -----------------------
|
||||||
|
#
|
||||||
|
# update-environment has the default " ," separator, so a single string value
|
||||||
|
# is split into consecutive indices starting at 0.
|
||||||
|
check_ok set -g update-environment "AAA BBB,CCC"
|
||||||
|
check_array "-g update-environment" "update-environment[0] AAA
|
||||||
|
update-environment[1] BBB
|
||||||
|
update-environment[2] CCC"
|
||||||
|
|
||||||
|
# --- -a append goes to the next free index --------------------------------
|
||||||
|
check_ok set -ga update-environment "DDD"
|
||||||
|
check_array "-g update-environment" "update-environment[0] AAA
|
||||||
|
update-environment[1] BBB
|
||||||
|
update-environment[2] CCC
|
||||||
|
update-environment[3] DDD"
|
||||||
|
|
||||||
|
# --- per-index unset leaves a gap; show preserves order and gaps ----------
|
||||||
|
check_ok set -gu update-environment[1]
|
||||||
|
check_array "-g update-environment" "update-environment[0] AAA
|
||||||
|
update-environment[2] CCC
|
||||||
|
update-environment[3] DDD"
|
||||||
|
# show -v of an existing index returns its value; a missing index is empty.
|
||||||
|
check_value "-gv update-environment[0]" "AAA"
|
||||||
|
check_value "-gv update-environment[1]" ""
|
||||||
|
|
||||||
|
# --- explicit indexed set, including out-of-order and gaps ----------------
|
||||||
|
#
|
||||||
|
# status-format is a session array; assigning an empty string first clears its
|
||||||
|
# multi-index default, then set specific indices out of order and confirm show
|
||||||
|
# sorts by ascending index and keeps the gap at [1].
|
||||||
|
check_ok set -g status-format ""
|
||||||
|
check_array "-g status-format" "status-format"
|
||||||
|
check_ok set -g status-format[5] "five"
|
||||||
|
check_ok set -g status-format[0] "zero"
|
||||||
|
check_ok set -g status-format[2] "two"
|
||||||
|
check_array "-g status-format" "status-format[0] zero
|
||||||
|
status-format[2] two
|
||||||
|
status-format[5] five"
|
||||||
|
|
||||||
|
# --- comma-only separator (user-keys) -------------------------------------
|
||||||
|
#
|
||||||
|
# user-keys splits only on comma, so an embedded space stays within one entry
|
||||||
|
# (and show quotes a value containing a space).
|
||||||
|
check_ok set -g user-keys "One,Two Three"
|
||||||
|
check_array "-g user-keys" 'user-keys[0] One
|
||||||
|
user-keys[1] "Two Three"'
|
||||||
|
|
||||||
|
# --- command-type array (a hook) ------------------------------------------
|
||||||
|
#
|
||||||
|
# Hooks are command arrays: an indexed value is parsed as a command when set
|
||||||
|
# and re-printed from the parsed command list; a syntax error is reported.
|
||||||
|
check_ok set -g alert-bell[0] "display-message hi"
|
||||||
|
check_value "-gv alert-bell[0]" "display-message hi"
|
||||||
|
check_fail "syntax error" set -g alert-bell[0] "if -x {"
|
||||||
|
|
||||||
|
# --- colour-type array ----------------------------------------------------
|
||||||
|
#
|
||||||
|
# pane-colours is a colour array; an indexed value is validated as a colour.
|
||||||
|
check_ok set -w pane-colours[0] red
|
||||||
|
check_value "-wv pane-colours[0]" "red"
|
||||||
|
check_fail "bad colour: xxxyyy" set -w pane-colours[1] xxxyyy
|
||||||
|
|
||||||
|
# --- -o refuses to overwrite an already-set index -------------------------
|
||||||
|
check_ok set -g command-alias[9] "x=list-keys"
|
||||||
|
check_fail "already set: command-alias[9]" set -go command-alias[9] "y=list-keys"
|
||||||
|
|
||||||
|
# --- non-array option rejects index syntax --------------------------------
|
||||||
|
#
|
||||||
|
# status-left is a plain string; indexing it is an error.
|
||||||
|
check_fail "not an array: status-left[0]" set -g status-left[0] "x"
|
||||||
|
|
||||||
|
assert_alive "after options-array tests"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
208
regress/options-scope.sh
Normal file
208
regress/options-scope.sh
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of the options engine scoping and inheritance, as described in the
|
||||||
|
# OPTIONS section of tmux(1) and implemented in options.c, cmd-set-option.c and
|
||||||
|
# cmd-show-options.c.
|
||||||
|
#
|
||||||
|
# This exercises: global vs session vs window vs pane precedence; -u to remove
|
||||||
|
# an option (revealing the inherited value); -gu to restore a global option to
|
||||||
|
# its compiled default; scope inference from the option name (-w/-p and the
|
||||||
|
# set-window-option alias); show -v (which does NOT walk parents) versus show -A
|
||||||
|
# (which does, marking inherited values with a trailing *); unknown/ambiguous
|
||||||
|
# option errors and -q suppression; and user options (@foo) at every scope.
|
||||||
|
#
|
||||||
|
# options-values.sh covers value validation and options-array.sh covers arrays.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# check_value $args $expected
|
||||||
|
#
|
||||||
|
# Run show-option with $args and compare the single-line output with $expected.
|
||||||
|
check_value()
|
||||||
|
{
|
||||||
|
out=$($TMUX show $1 2>&1)
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "show $1 failed."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# check_ok $cmd...
|
||||||
|
#
|
||||||
|
# Run a command and require that it succeeds.
|
||||||
|
check_ok()
|
||||||
|
{
|
||||||
|
if ! $TMUX "$@"; then
|
||||||
|
echo "Command failed (expected success): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# check_fail $expected_error $cmd...
|
||||||
|
#
|
||||||
|
# Run a command and require that it fails with the given error message.
|
||||||
|
check_fail()
|
||||||
|
{
|
||||||
|
exp="$1"
|
||||||
|
shift
|
||||||
|
out=$($TMUX "$@" 2>&1)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Command succeeded (expected failure): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Wrong error for: $*"
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server died: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# --- global vs session precedence -----------------------------------------
|
||||||
|
#
|
||||||
|
# status-left is a session option. A value set at the session scope shadows
|
||||||
|
# the global one; show -v at each scope reports that scope's own value.
|
||||||
|
check_ok set -g status-left "GLOBAL"
|
||||||
|
check_ok set status-left "SESSION"
|
||||||
|
check_value "-v status-left" "SESSION"
|
||||||
|
check_value "-gv status-left" "GLOBAL"
|
||||||
|
|
||||||
|
# show -v does NOT inherit: -u removes the session entry, after which the
|
||||||
|
# session-scope show -v is empty even though the global value still exists.
|
||||||
|
check_ok set -u status-left
|
||||||
|
check_value "-v status-left" ""
|
||||||
|
check_value "-gv status-left" "GLOBAL"
|
||||||
|
|
||||||
|
# show -A walks the parent scopes and marks an inherited value with a "*".
|
||||||
|
out=$($TMUX show -A 2>/dev/null | grep '^status-left\*')
|
||||||
|
if [ "$out" != "status-left* GLOBAL" ]; then
|
||||||
|
echo "show -A did not mark inherited status-left."
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- -gu restores the compiled default ------------------------------------
|
||||||
|
#
|
||||||
|
# Removing a global option with -u restores its built-in default rather than
|
||||||
|
# deleting it; status-left's default is the format "[#{session_name}] ".
|
||||||
|
check_ok set -g status-left "GLOBAL2"
|
||||||
|
check_value "-gv status-left" "GLOBAL2"
|
||||||
|
check_ok set -gu status-left
|
||||||
|
check_value "-gv status-left" "[#{session_name}] "
|
||||||
|
|
||||||
|
# --- scope inference from the option name ---------------------------------
|
||||||
|
#
|
||||||
|
# mode-keys is a window option, so a bare set-option infers the window scope;
|
||||||
|
# set-window-option (setw) is an explicit alias for the same thing, and -g w
|
||||||
|
# targets the global window options.
|
||||||
|
check_ok set mode-keys vi
|
||||||
|
check_value "-wv mode-keys" "vi"
|
||||||
|
check_ok setw mode-keys emacs
|
||||||
|
check_value "-wv mode-keys" "emacs"
|
||||||
|
check_ok set -gw mode-keys vi
|
||||||
|
check_value "-gwv mode-keys" "vi"
|
||||||
|
|
||||||
|
# cursor-colour is a window-and-pane option. A pane-scope value overrides a
|
||||||
|
# window-scope one for that pane.
|
||||||
|
check_ok set -w cursor-colour blue
|
||||||
|
check_ok set -p cursor-colour red
|
||||||
|
check_value "-pv cursor-colour" "red"
|
||||||
|
out=$($TMUX show -Ap 2>/dev/null | grep '^cursor-colour ')
|
||||||
|
if [ "$out" != "cursor-colour red" ]; then
|
||||||
|
echo "pane cursor-colour did not override window value."
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- -U unsets a window option and clears pane copies ----------------------
|
||||||
|
#
|
||||||
|
# When a window option also has per-pane copies, -u on the window scope leaves
|
||||||
|
# those pane copies in place; -U additionally removes the option from every
|
||||||
|
# pane in the window, so all panes fall back to inheritance.
|
||||||
|
$TMUX split-window -t main || exit 1
|
||||||
|
panes=$($TMUX list-panes -t main -F '#{pane_id}')
|
||||||
|
set -- $panes
|
||||||
|
pa=$1
|
||||||
|
pb=$2
|
||||||
|
check_ok set -p -t "$pa" cursor-colour red
|
||||||
|
check_ok set -p -t "$pb" cursor-colour blue
|
||||||
|
check_ok set -w -t main cursor-colour green
|
||||||
|
check_value "-pv -t $pa cursor-colour" "red"
|
||||||
|
check_value "-pv -t $pb cursor-colour" "blue"
|
||||||
|
check_value "-wv -t main cursor-colour" "green"
|
||||||
|
check_ok set -Uw -t main cursor-colour
|
||||||
|
check_value "-pv -t $pa cursor-colour" ""
|
||||||
|
check_value "-pv -t $pb cursor-colour" ""
|
||||||
|
check_value "-wv -t main cursor-colour" ""
|
||||||
|
|
||||||
|
# --- unknown, ambiguous and -q --------------------------------------------
|
||||||
|
check_fail "invalid option: no-such-option" set -g no-such-option x
|
||||||
|
check_fail "ambiguous option: status-l" set -g status-l x
|
||||||
|
# A unique prefix resolves to the full option name.
|
||||||
|
check_ok set -g status-inte 5
|
||||||
|
check_value "-gv status-interval" "5"
|
||||||
|
# -q suppresses the error and exits successfully.
|
||||||
|
check_ok set -gq no-such-option x
|
||||||
|
check_ok show -gqv no-such-option
|
||||||
|
|
||||||
|
# --- errors from unresolvable targets -------------------------------------
|
||||||
|
#
|
||||||
|
# A -t target that does not resolve produces a scope-specific error from
|
||||||
|
# options_scope_from_name()/options_scope_from_flags().
|
||||||
|
check_fail "no such session: nosuch" show -t nosuch status-left
|
||||||
|
check_fail "no such window: nosuch" show -w -t nosuch mode-keys
|
||||||
|
check_fail "no such pane: nosuch" set -p -t nosuch cursor-colour red
|
||||||
|
|
||||||
|
# --- show with no option name lists every option --------------------------
|
||||||
|
#
|
||||||
|
# show without a specific option walks the whole table (cmd_show_options_all).
|
||||||
|
# Hooks are hidden unless -H is given.
|
||||||
|
$TMUX set -g @listme "here" || exit 1
|
||||||
|
if ! $TMUX show -g | grep -q '^@listme here$'; then
|
||||||
|
echo "show -g did not list @listme."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# alert-bell is a hook: only shown with -H.
|
||||||
|
if $TMUX show -g | grep -q '^alert-bell'; then
|
||||||
|
echo "show -g listed a hook without -H."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! $TMUX show -gH | grep -q '^alert-bell'; then
|
||||||
|
echo "show -gH did not list the alert-bell hook."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- user options at every scope ------------------------------------------
|
||||||
|
#
|
||||||
|
# @-prefixed user options can be created freely at any scope and do not
|
||||||
|
# inherit type checking.
|
||||||
|
check_ok set -g @u "global-user"
|
||||||
|
check_ok set @u "session-user"
|
||||||
|
check_ok set -w @u "window-user"
|
||||||
|
check_ok set -p @u "pane-user"
|
||||||
|
check_value "-gv @u" "global-user"
|
||||||
|
check_value "-v @u" "session-user"
|
||||||
|
check_value "-wv @u" "window-user"
|
||||||
|
check_value "-pv @u" "pane-user"
|
||||||
|
|
||||||
|
assert_alive "after options-scope tests"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
193
regress/options-values.sh
Normal file
193
regress/options-values.sh
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of options engine value validation, as implemented by
|
||||||
|
# options_from_string() and friends in options.c.
|
||||||
|
#
|
||||||
|
# Each option table entry has a type (string, number, key, colour, flag,
|
||||||
|
# choice, command) with type-specific parsing and validation. This exercises:
|
||||||
|
# number range limits; choice options rejecting unknown values; flag options
|
||||||
|
# toggling with no value and rejecting garbage; colour and key options
|
||||||
|
# rejecting invalid input; string append with -a; -F expansion at set time;
|
||||||
|
# and -o refusing to overwrite an option that is already set.
|
||||||
|
#
|
||||||
|
# options-scope.sh covers scoping/inheritance and options-array.sh covers
|
||||||
|
# arrays.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
check_value()
|
||||||
|
{
|
||||||
|
out=$($TMUX show $1 2>&1)
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "show $1 failed."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ok()
|
||||||
|
{
|
||||||
|
if ! $TMUX "$@"; then
|
||||||
|
echo "Command failed (expected success): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_fail()
|
||||||
|
{
|
||||||
|
exp="$1"
|
||||||
|
shift
|
||||||
|
out=$($TMUX "$@" 2>&1)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Command succeeded (expected failure): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Wrong error for: $*"
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server died: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# --- number options -------------------------------------------------------
|
||||||
|
#
|
||||||
|
# display-time is a number with a minimum of 0; a negative value and a
|
||||||
|
# non-numeric value are both rejected via strtonum(3).
|
||||||
|
check_ok set -g display-time 4000
|
||||||
|
check_value "-gv display-time" "4000"
|
||||||
|
check_fail "value is too small: -5" set -g display-time -5
|
||||||
|
check_fail "value is invalid: abc" set -g display-time abc
|
||||||
|
# A missing value is rejected for a non-flag, non-choice option.
|
||||||
|
check_fail "empty value" set -g display-time
|
||||||
|
|
||||||
|
# --- choice options -------------------------------------------------------
|
||||||
|
#
|
||||||
|
# status-keys accepts only its listed choices (vi/emacs); anything else is an
|
||||||
|
# "unknown value" error and the option keeps its previous value.
|
||||||
|
check_ok set -g status-keys vi
|
||||||
|
check_value "-gv status-keys" "vi"
|
||||||
|
check_fail "unknown value: bogus" set -g status-keys bogus
|
||||||
|
check_value "-gv status-keys" "vi"
|
||||||
|
|
||||||
|
# --- flag options ---------------------------------------------------------
|
||||||
|
#
|
||||||
|
# focus-events is an on/off flag. Setting with no value toggles it; explicit
|
||||||
|
# on/off/yes/no/1/0 are accepted (case-insensitively); anything else fails.
|
||||||
|
check_ok set -g focus-events off
|
||||||
|
check_value "-gv focus-events" "off"
|
||||||
|
check_ok set -g focus-events # toggle
|
||||||
|
check_value "-gv focus-events" "on"
|
||||||
|
check_ok set -g focus-events # toggle back
|
||||||
|
check_value "-gv focus-events" "off"
|
||||||
|
check_ok set -g focus-events yes
|
||||||
|
check_value "-gv focus-events" "on"
|
||||||
|
check_ok set -g focus-events NO
|
||||||
|
check_value "-gv focus-events" "off"
|
||||||
|
check_fail "bad value: maybe" set -g focus-events maybe
|
||||||
|
|
||||||
|
# --- colour options -------------------------------------------------------
|
||||||
|
#
|
||||||
|
# status-bg is a colour; named colours, numbers and #rrggbb are accepted,
|
||||||
|
# garbage is rejected.
|
||||||
|
check_ok set -g status-bg red
|
||||||
|
check_value "-gv status-bg" "red"
|
||||||
|
check_ok set -g status-bg colour123
|
||||||
|
check_value "-gv status-bg" "colour123"
|
||||||
|
check_ok set -g status-bg "#00ff00"
|
||||||
|
check_value "-gv status-bg" "#00ff00"
|
||||||
|
check_fail "bad colour: xxxyyy" set -g status-bg xxxyyy
|
||||||
|
|
||||||
|
# --- style options --------------------------------------------------------
|
||||||
|
#
|
||||||
|
# status-style is a style string, validated when set; a bogus style keyword is
|
||||||
|
# rejected and the old value is retained.
|
||||||
|
check_ok set -g status-style "fg=red,bg=black"
|
||||||
|
check_value "-gv status-style" "fg=red,bg=black"
|
||||||
|
check_fail "invalid style: bg=xxxyyy" set -g status-style "bg=xxxyyy"
|
||||||
|
check_value "-gv status-style" "fg=red,bg=black"
|
||||||
|
|
||||||
|
# --- key options ----------------------------------------------------------
|
||||||
|
#
|
||||||
|
# prefix is a key; a valid key name is stored in canonical form, a bad one is
|
||||||
|
# rejected.
|
||||||
|
check_ok set -g prefix C-a
|
||||||
|
check_value "-gv prefix" "C-a"
|
||||||
|
check_fail "bad key: boguskey" set -g prefix boguskey
|
||||||
|
|
||||||
|
# --- string options with extra validation ---------------------------------
|
||||||
|
#
|
||||||
|
# default-shell is a string but is checked to be an executable shell; a bogus
|
||||||
|
# path is rejected and the old value kept.
|
||||||
|
old=$($TMUX show -gv default-shell)
|
||||||
|
check_fail "not a suitable shell: /not/a/shell" set -g default-shell /not/a/shell
|
||||||
|
check_value "-gv default-shell" "$old"
|
||||||
|
|
||||||
|
# --- user options require a value ------------------------------------------
|
||||||
|
#
|
||||||
|
# A user option set with no value at all is an error.
|
||||||
|
check_fail "empty value" set -g @novalue
|
||||||
|
|
||||||
|
# --- command options ------------------------------------------------------
|
||||||
|
#
|
||||||
|
# default-client-command is a command option: the value is parsed as a tmux
|
||||||
|
# command when set and re-printed from the parsed command list. A syntax
|
||||||
|
# error is reported and the option is left unchanged.
|
||||||
|
check_ok set -g default-client-command "new-window"
|
||||||
|
check_value "-gv default-client-command" "new-window"
|
||||||
|
check_fail "syntax error" set -g default-client-command "if -x {"
|
||||||
|
check_value "-gv default-client-command" "new-window"
|
||||||
|
|
||||||
|
# --- renamed option aliases -----------------------------------------------
|
||||||
|
#
|
||||||
|
# Historical option names are mapped to their current spelling, so setting
|
||||||
|
# cursor-color updates cursor-colour.
|
||||||
|
check_ok set -w cursor-color red
|
||||||
|
check_value "-wv cursor-colour" "red"
|
||||||
|
|
||||||
|
# --- string append (-a) ---------------------------------------------------
|
||||||
|
#
|
||||||
|
# -a appends to the current string value rather than replacing it.
|
||||||
|
check_ok set -g @str "foo"
|
||||||
|
check_ok set -ga @str "bar"
|
||||||
|
check_value "-gv @str" "foobar"
|
||||||
|
|
||||||
|
# --- -F expands at set time -----------------------------------------------
|
||||||
|
#
|
||||||
|
# With -F the value is expanded as a format once, at set time; without -F it is
|
||||||
|
# stored literally.
|
||||||
|
check_ok set -gF @expanded "#{session_name}"
|
||||||
|
check_value "-gv @expanded" "main"
|
||||||
|
check_ok set -g @literal "#{session_name}"
|
||||||
|
check_value "-gv @literal" "#{session_name}"
|
||||||
|
|
||||||
|
# --- -o refuses to overwrite ----------------------------------------------
|
||||||
|
#
|
||||||
|
# -o makes set-option fail if the option is already set, leaving it unchanged;
|
||||||
|
# it succeeds for an option that is not yet set.
|
||||||
|
check_ok set -g @once "first"
|
||||||
|
check_fail "already set: @once" set -go @once "second"
|
||||||
|
check_value "-gv @once" "first"
|
||||||
|
check_ok set -go @fresh "value"
|
||||||
|
check_value "-gv @fresh" "value"
|
||||||
|
|
||||||
|
assert_alive "after options-values tests"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
@@ -131,11 +131,14 @@ $IN send-keys -l "Z" || exit 1
|
|||||||
settle
|
settle
|
||||||
search_is "hello Z" "C-w did not kill a word"
|
search_is "hello Z" "C-w did not kill a word"
|
||||||
|
|
||||||
# C-a then C-k kills the whole line.
|
# C-a then C-k kills the whole line. The mode prompt no longer fills the rest
|
||||||
|
# of the row, so insert a marker to distinguish prompt input from tree content
|
||||||
|
# that may remain visible after the prompt.
|
||||||
$IN send-keys C-a || exit 1
|
$IN send-keys C-a || exit 1
|
||||||
$IN send-keys C-k || exit 1
|
$IN send-keys C-k || exit 1
|
||||||
|
$IN send-keys -l "X" || exit 1
|
||||||
settle
|
settle
|
||||||
search_row | grep -q '(search) [^ ]' && fail "C-a C-k did not clear the line"
|
search_is "X" "C-a C-k did not clear the line"
|
||||||
|
|
||||||
# --- 3. Editing kept the prompt open the whole time. ---
|
# --- 3. Editing kept the prompt open the whole time. ---
|
||||||
in_tree_mode || fail "editing keys closed the mode"
|
in_tree_mode || fail "editing keys closed the mode"
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[31m│[39m
|
[31m│[39m
|
||||||
[32m┌────────────────┐[39m
|
[31m│[39m
|
||||||
[32m│[39m [32m│[39m
|
[32m┌──────────────┐[39m
|
||||||
[32m│[39m [32m│[39m
|
[32m│[39m [32m│[39m
|
||||||
[32m│[39m [32m│[39m
|
[32m│[39m [32m│[39m
|
||||||
[32m│[39m [32m│[39m
|
[32m│[39m [32m│[39m
|
||||||
[32m│[39m [32m│[39m
|
[32m│[39m [32m│[39m
|
||||||
[32m│[39m [32m│[39m
|
[32m└──────────────┘[39m
|
||||||
[32m└────────────────┘[39m
|
[32m│[39m
|
||||||
[32m│[39m
|
[32m│[39m
|
||||||
[32m│[39m
|
[32m│[39m
|
||||||
[32m│[39m
|
[32m│[39m
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
base
|
base
|
||||||
|
|
||||||
┌────────────────────┐
|
|
||||||
│FLOAT │
|
┌──────────────────┐
|
||||||
│ │
|
│FLOAT │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
└──────────────────┘
|
||||||
└────────────────────┘
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
bas╔════════════════════════════╗
|
base
|
||||||
║FLOAT ║
|
╔══════════════════════════╗
|
||||||
║ ║
|
║FLOAT ║
|
||||||
║ ║
|
║ ║
|
||||||
║ ║
|
║ ║
|
||||||
║ ║
|
║ ║
|
||||||
║ ║
|
║ ║
|
||||||
║ ║
|
║ ║
|
||||||
║ ║
|
╚══════════════════════════╝
|
||||||
╚════════════════════════════╝
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
06:abcdefghijklmnopqrstuvwxyz0123456789
|
06:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
07:abcdefghijklmnopqrstuvwxyz0123456789
|
07:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
08:abcdefghijklmnopqrstuvwxyz0123456789
|
08:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
09:abcdef┌────────────────────┐23456789
|
09:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
10:abcdef│BOTTOM │23456789
|
10:abcdefg┌──────────────────┐123456789
|
||||||
11:abcdef│ │23456789
|
11:abcdefg│BOTTOM │123456789
|
||||||
│ │
|
│ │
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ base
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
┌──────────
|
|
||||||
│CLIP
|
┌─────────
|
||||||
│
|
│CLIP
|
||||||
│
|
│
|
||||||
│
|
│
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
01:abcdefghijklmnopqrstuvwxyz0123456789
|
01:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
02:abcdefghijklmnopqrstuvwxyz0123456789
|
02:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
──────────┐ijklmnopqrstuvwxyz0123456789
|
03:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
│ijklmnopqrstuvwxyz0123456789
|
─────────┐hijklmnopqrstuvwxyz0123456789
|
||||||
│ijklmnopqrstuvwxyz0123456789
|
│hijklmnopqrstuvwxyz0123456789
|
||||||
│ijklmnopqrstuvwxyz0123456789
|
│hijklmnopqrstuvwxyz0123456789
|
||||||
│ijklmnopqrstuvwxyz0123456789
|
│hijklmnopqrstuvwxyz0123456789
|
||||||
│ijklmnopqrstuvwxyz0123456789
|
│hijklmnopqrstuvwxyz0123456789
|
||||||
│ijklmnopqrstuvwxyz0123456789
|
─────────┘hijklmnopqrstuvwxyz0123456789
|
||||||
──────────┘ijklmnopqrstuvwxyz0123456789
|
10:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
11:abcdefghijklmnopqrstuvwxyz0123456789
|
11:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
01:abcdefghijklmnopqrstuvwxyz0123456789
|
01:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
02:abcdefghijklmnopqrstuvwxyz0123456789
|
02:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
03:abcdefghijklmnopqrstuvwxyz┌──────────
|
03:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
04:abcdefghijklmnopqrstuvwxyz│RIGHT
|
04:abcdefghijklmnopqrstuvwxyz0┌─────────
|
||||||
05:abcdefghijklmnopqrstuvwxyz│
|
05:abcdefghijklmnopqrstuvwxyz0│RIGHT
|
||||||
06:abcdefghijklmnopqrstuvwxyz│
|
06:abcdefghijklmnopqrstuvwxyz0│
|
||||||
07:abcdefghijklmnopqrstuvwxyz│
|
07:abcdefghijklmnopqrstuvwxyz0│
|
||||||
08:abcdefghijklmnopqrstuvwxyz│
|
08:abcdefghijklmnopqrstuvwxyz0│
|
||||||
09:abcdefghijklmnopqrstuvwxyz│
|
09:abcdefghijklmnopqrstuvwxyz0└─────────
|
||||||
10:abcdefghijklmnopqrstuvwxyz└──────────
|
10:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
11:abcdefghijklmnopqrstuvwxyz0123456789
|
11:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
01:abcdef│ │23456789
|
01:abcdefg│ │123456789
|
||||||
02:abcdef│ │23456789
|
02:abcdefg│ │123456789
|
||||||
03:abcdef│ │23456789
|
03:abcdefg└──────────────────┘123456789
|
||||||
04:abcdef└────────────────────┘23456789
|
04:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
05:abcdefghijklmnopqrstuvwxyz0123456789
|
05:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
06:abcdefghijklmnopqrstuvwxyz0123456789
|
06:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
07:abcdefghijklmnopqrstuvwxyz0123456789
|
07:abcdefghijklmnopqrstuvwxyz0123456789
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
│
|
│
|
||||||
│
|
│
|
||||||
│
|
│
|
||||||
│
|
─────────────┘
|
||||||
──────────────┘
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
········································
|
········································
|
||||||
········································
|
········································
|
||||||
·······┌────────────────────┐···········
|
········································
|
||||||
·······│FLOAT │···········
|
········┌──────────────────┐············
|
||||||
·······│ │···········
|
········│FLOAT │············
|
||||||
·······│ │···········
|
········│ │············
|
||||||
·······│ │···········
|
········│ │············
|
||||||
·······│ │···········
|
········│ │············
|
||||||
·······│ │···········
|
········└──────────────────┘············
|
||||||
·······└────────────────────┘···········
|
········································
|
||||||
········································
|
········································
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
ba┌── one ─────────┐
|
base
|
||||||
│ONE │
|
┌── one ───────┐
|
||||||
│ │
|
│ONE │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
└──────────────┘
|
||||||
└──────────────┏━━━━━━━━━━━━━━━━┓
|
|
||||||
┃TWO ┃
|
┏━━━━━━━━━━━━━━┓
|
||||||
┃ ┃
|
┃TWO ┃
|
||||||
┃ ┃
|
┃ ┃
|
||||||
┃ ┃
|
┗━━ two ━━━━━━━┛
|
||||||
┗━━ two ━━━━━━━━━┛
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
base │···········
|
base │···········
|
||||||
│···········
|
│···········
|
||||||
│···········
|
│···········
|
||||||
┌──────────│···········
|
│···········
|
||||||
│OUT │···········
|
┌─────────│···········
|
||||||
│ │···········
|
│OUT │···········
|
||||||
│ │···········
|
│ │···········
|
||||||
│ │···········
|
│ │···········
|
||||||
────────────────────────────┘···········
|
──────────────────└─────────┘···········
|
||||||
········································
|
········································
|
||||||
········································
|
········································
|
||||||
········································
|
········································
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
base [30m[47m
|
base [30m[47m
|
||||||
[39m[49m [30m[47m
|
[39m[49m [30m[47m
|
||||||
[39m[49m [32m┌────────────────┐[30m[47m
|
[39m[49m [30m[47m
|
||||||
[39m[49m [32m│[39mOVERSB [30m[47m [32m[49m│[30m[47m
|
[39m[49m [38;2;154;205;50m┌──────────────┐[39m [30m[47m
|
||||||
[39m[49m [32m│[39m [30m[47m [32m[49m│[30m[47m
|
[39m[49m [38;2;154;205;50m│[39mOVERSB [30m[47m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49m [32m│[39m [30m[47m [32m[49m│[30m[47m
|
[39m[49m [38;2;154;205;50m│[39m [30m[47m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49m [32m│[39m [30m[47m [32m[49m│[30m[47m
|
[39m[49m [38;2;154;205;50m│[39m [30m[47m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49m [32m│[39m [30m[47m [32m[49m│[30m[47m
|
[39m[49m [38;2;154;205;50m│[39m [30m[47m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49m [32m│[39m [30m[47m [32m[49m│[30m[47m
|
[39m[49m [38;2;154;205;50m└──────────────┘[39m [30m[47m
|
||||||
[39m[49m [32m└────────────────┘[30m[47m
|
[39m[49m [30m[47m
|
||||||
[39m[49m [30m[47m
|
[39m[49m [30m[47m
|
||||||
[39m[49m [30m[47m
|
[39m[49m [30m[47m
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ base
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
┌────────────────────┐
|
|
||||||
│OVERST │
|
┌──────────────────┐
|
||||||
│ │
|
│OVERST │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
TILE01 abcdefghij │TILE01 abcdefghij
|
TILE01 abcdefghij │TILE01 abcdefghij
|
||||||
TILE02 abcdefghij │TILE02 abcdefghij
|
TILE02 abcdefghij │TILE02 abcdefghij
|
||||||
TILE03 abc╔════════════════╗bcdefghij
|
TILE03 abcdefghij │TILE03 abcdefghij
|
||||||
TILE04 abc║FLT ║bcdefghij
|
TILE04 abcd╔══════════════╗abcdefghij
|
||||||
║ ║
|
║FLT ║
|
||||||
──────────║ ║────────────
|
───────────║ ║─────────────
|
||||||
TILE00 abc║ ║bcdefghij
|
TILE00 abcd║ ║abcdefghij
|
||||||
TILE01 abc║ ║bcdefghij
|
TILE01 abcd║ ║abcdefghij
|
||||||
TILE02 abc║ ║bcdefghij
|
TILE02 abcd╚══════════════╝abcdefghij
|
||||||
TILE03 abc╚════════════════╝bcdefghij
|
TILE03 abcdefghij │TILE03 abcdefghij
|
||||||
TILE04 abcdefghij │TILE04 abcdefghij
|
TILE04 abcdefghij │TILE04 abcdefghij
|
||||||
│
|
│
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
TILE01 abcdefghij │TILE01 abcdefghij
|
TILE01 abcdefghij │TILE01 abcdefghij
|
||||||
TILE02 abcdefghij │TILE02 abcdefghij
|
TILE02 abcdefghij │TILE02 abcdefghij
|
||||||
TILE03 abc┌────────────────┐bcdefghij
|
TILE03 abcdefghij │TILE03 abcdefghij
|
||||||
TILE04 abc│FLT │bcdefghij
|
TILE04 abcd┌──────────────┐abcdefghij
|
||||||
│ │
|
│FLT │
|
||||||
──────────│ │────────────
|
───────────│ │─────────────
|
||||||
TILE00 abc│ │bcdefghij
|
TILE00 abcd│ │abcdefghij
|
||||||
TILE01 abc│ │bcdefghij
|
TILE01 abcd│ │abcdefghij
|
||||||
TILE02 abc│ │bcdefghij
|
TILE02 abcd└──────────────┘abcdefghij
|
||||||
TILE03 abc└────────────────┘bcdefghij
|
TILE03 abcdefghij │TILE03 abcdefghij
|
||||||
TILE04 abcdefghij │TILE04 abcdefghij
|
TILE04 abcdefghij │TILE04 abcdefghij
|
||||||
│
|
│
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
base
|
base
|
||||||
┌────────────────┐
|
|
||||||
│AAA │
|
┌──────────────┐
|
||||||
│ │
|
│AAA │
|
||||||
│ │
|
│ │
|
||||||
│ ┌────────────────┐
|
│ │
|
||||||
│ │BBB │
|
│ ┌──────────────┐
|
||||||
│ │ │
|
└─────────│BBB │
|
||||||
└─────────│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
└──────────────┘
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
── base ────────────────────────────────
|
── base ────────────────────────────────
|
||||||
base ┌── title ───────────┐
|
base
|
||||||
│FLOAT │
|
┌── title ─────────┐
|
||||||
│ │
|
│FLOAT │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
└──────────────────┘
|
||||||
└────────────────────┘
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
[7m[32m┌────────────────────┐[0m
|
|
||||||
[7m[32m│[0m [7m[32m│[0m
|
[7m[38;2;147;112;219m┌──────────────────┐[0m
|
||||||
[7m[32m│[0m [7m[32m│[0m
|
[7m[38;2;147;112;219m│[0m [7m[38;2;147;112;219m│[0m
|
||||||
[7m[32m│[0m [7m[32m│[0m
|
[7m[38;2;147;112;219m│[0m [7m[38;2;147;112;219m│[0m
|
||||||
[7m[32m│[0m [7m[32m│[0m
|
[7m[38;2;147;112;219m│[0m [7m[38;2;147;112;219m│[0m
|
||||||
[7m[32m│[0m [7m[32m│[0m
|
[7m[38;2;147;112;219m│[0m [7m[38;2;147;112;219m│[0m
|
||||||
[7m[32m│[0m [7m[32m│[0m
|
[7m[38;2;147;112;219m└──────────────────┘[0m
|
||||||
[7m[32m└────────────────────┘[0m
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[32m── 0:left ──────────[7m┬[39m──[0m 1:right ────────
|
[38;2;154;205;50m── 0:left ──────────[7m┬[38;2;179;179;179m──[0m[38;2;179;179;179m 1:right ────────
|
||||||
[7m[32m│[0m
|
[39m [7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
[7m[32m│[0m
|
[7m[38;2;154;205;50m│[0m
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[7m[32m────────────────────────────────────────
|
[7m[38;2;154;205;50m────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
[7m[32m│[0m [7m│[0m
|
[7m[38;2;154;205;50m│[0m [7m[38;2;179;179;179m│[0m
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
SB00 abcdefghij [30m[47m
|
SB00 abcdefghij [30m[47m
|
||||||
[39m[49mSB01 abcdefghij [30m[47m
|
[39m[49mSB01 abcdefghij [30m[47m
|
||||||
[39m[49mSB02 ab[32m┌────────────────────┐[39m [30m[47m
|
[39m[49mSB02 abcdefghij [30m[47m
|
||||||
[39m[49mSB03 ab[32m│[39mFLOAT00 abcdef [30m[47m [32m[49m│[39m [30m[47m
|
[39m[49mSB03 abc[38;2;154;205;50m┌──────────────────┐[39m [30m[47m
|
||||||
[39m[49mSB04 ab[32m│[39mFLOAT01 abcdef [30m[47m [32m[49m│[39m [30m[47m
|
[39m[49mSB04 abc[38;2;154;205;50m│[39mFLOAT02 abcdef [37m[40m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49mSB05 ab[32m│[39mFLOAT02 abcdef [30m[47m [32m[49m│[39m [30m[47m
|
[39m[49mSB05 abc[38;2;154;205;50m│[39mFLOAT03 abcdef [37m[40m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49mSB06 ab[32m│[39mFLOAT03 abcdef [30m[47m [32m[49m│[39m [30m[47m
|
[39m[49mSB06 abc[38;2;154;205;50m│[39mFLOAT04 abcdef [30m[47m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49mSB07 ab[32m│[39mFLOAT04 abcdef [30m[47m [32m[49m│[39m [30m[47m
|
[39m[49mSB07 abc[38;2;154;205;50m│[39m [30m[47m [38;2;154;205;50m[49m│[39m [30m[47m
|
||||||
[39m[49mSB08 ab[32m│[39m [30m[47m [32m[49m│[39m [30m[47m
|
[39m[49mSB08 abc[38;2;154;205;50m└──────────────────┘[39m [30m[47m
|
||||||
[39m[49mSB09 ab[32m└────────────────────┘[39m [30m[47m
|
[39m[49mSB09 abcdefghij [30m[47m
|
||||||
[39m[49mSB10 abcdefghij [30m[47m
|
[39m[49mSB10 abcdefghij [30m[47m
|
||||||
[39m[49m [30m[47m
|
[39m[49m [30m[47m
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[30m[47m [39m[49mSB00 abcdefghij [32m│[30m[47m [39m[49mSBL00 abcdefghij
|
[30m[47m [39m[49mSB00 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL00 abcdefghij
|
||||||
[30m[47m [39m[49mSB01 abcdefghij [32m│[30m[47m [39m[49mSBL01 abcdefghij
|
[30m[47m [39m[49mSB01 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL01 abcdefghij
|
||||||
[30m[47m [39m[49mSB02 abcdefghij [32m│[30m[47m [39m[49mSBL02 abcdefghij
|
[30m[47m [39m[49mSB02 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL02 abcdefghij
|
||||||
[30m[47m [39m[49mSB03 abcdefghij [32m│[30m[47m [39m[49mSBL03 abcdefghij
|
[30m[47m [39m[49mSB03 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL03 abcdefghij
|
||||||
[30m[47m [39m[49mSB04 abcdefghij [32m│[30m[47m [39m[49mSBL04 abcdefghij
|
[30m[47m [39m[49mSB04 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL04 abcdefghij
|
||||||
[30m[47m [39m[49mSB05 abcdefghij [32m│[30m[47m [39m[49mSBL05 abcdefghij
|
[30m[47m [39m[49mSB05 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL05 abcdefghij
|
||||||
[30m[47m [39m[49mSB06 abcdefghij [32m│[30m[47m [39m[49mSBL06 abcdefghij
|
[30m[47m [39m[49mSB06 abcdefghij [38;2;154;205;50m│[30m[47m [39m[49mSBL06 abcdefghij
|
||||||
[30m[47m [39m[49mSB07 abcdefghij │[30m[47m [39m[49mSBL07 abcdefghij
|
[30m[47m [39m[49mSB07 abcdefghij [38;2;179;179;179m│[30m[47m [39m[49mSBL07 abcdefghij
|
||||||
[30m[47m [39m[49mSB08 abcdefghij │[30m[47m [39m[49mSBL08 abcdefghij
|
[30m[47m [39m[49mSB08 abcdefghij [38;2;179;179;179m│[30m[47m [39m[49mSBL08 abcdefghij
|
||||||
[30m[47m [39m[49mSB09 abcdefghij │[30m[47m [39m[49mSBL09 abcdefghij
|
[30m[47m [39m[49mSB09 abcdefghij [38;2;179;179;179m│[30m[47m [39m[49mSBL09 abcdefghij
|
||||||
[30m[47m [39m[49mSB10 abcdefghij │[30m[47m [39m[49mSBL10 abcdefghij
|
[30m[47m [39m[49mSB10 abcdefghij [38;2;179;179;179m│[30m[47m [39m[49mSBL10 abcdefghij
|
||||||
[30m[47m [39m[49m │[30m[47m [39m[49m
|
[30m[47m [39m[49m [38;2;179;179;179m│[30m[47m [39m[49m
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
SB00 abcdefghij [30m[47m [32m[49m│[39mSBR00 abcdefghij [30m[47m
|
SB00 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR00 abcdefghij [30m[47m
|
||||||
[39m[49mSB01 abcdefghij [30m[47m [32m[49m│[39mSBR01 abcdefghij [30m[47m
|
[39m[49mSB01 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR01 abcdefghij [30m[47m
|
||||||
[39m[49mSB02 abcdefghij [30m[47m [32m[49m│[39mSBR02 abcdefghij [30m[47m
|
[39m[49mSB02 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR02 abcdefghij [30m[47m
|
||||||
[39m[49mSB03 abcdefghij [30m[47m [32m[49m│[39mSBR03 abcdefghij [30m[47m
|
[39m[49mSB03 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR03 abcdefghij [30m[47m
|
||||||
[39m[49mSB04 abcdefghij [30m[47m [32m[49m│[39mSBR04 abcdefghij [30m[47m
|
[39m[49mSB04 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR04 abcdefghij [30m[47m
|
||||||
[39m[49mSB05 abcdefghij [30m[47m [32m[49m│[39mSBR05 abcdefghij [30m[47m
|
[39m[49mSB05 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR05 abcdefghij [30m[47m
|
||||||
[39m[49mSB06 abcdefghij [30m[47m [32m[49m│[39mSBR06 abcdefghij [30m[47m
|
[39m[49mSB06 abcdefghij [30m[47m [38;2;154;205;50m[49m│[39mSBR06 abcdefghij [30m[47m
|
||||||
[39m[49mSB07 abcdefghij [30m[47m [39m[49m│SBR07 abcdefghij [30m[47m
|
[39m[49mSB07 abcdefghij [30m[47m [38;2;179;179;179m[49m│[39mSBR07 abcdefghij [30m[47m
|
||||||
[39m[49mSB08 abcdefghij [30m[47m [39m[49m│SBR08 abcdefghij [30m[47m
|
[39m[49mSB08 abcdefghij [30m[47m [38;2;179;179;179m[49m│[39mSBR08 abcdefghij [30m[47m
|
||||||
[39m[49mSB09 abcdefghij [30m[47m [39m[49m│SBR09 abcdefghij [30m[47m
|
[39m[49mSB09 abcdefghij [30m[47m [38;2;179;179;179m[49m│[39mSBR09 abcdefghij [30m[47m
|
||||||
[39m[49mSB10 abcdefghij [30m[47m [39m[49m│SBR10 abcdefghij [30m[47m
|
[39m[49mSB10 abcdefghij [30m[47m [38;2;179;179;179m[49m│[39mSBR10 abcdefghij [30m[47m
|
||||||
[39m[49m [30m[47m [39m[49m│ [30m[47m
|
[39m[49m [30m[47m [38;2;179;179;179m[49m│[39m [30m[47m
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[41mSTYLE00 abcdefghij [32m[49m│[39m[44mSTYLE00 abcdefghij
|
[41mSTYLE00 abcdefghij [38;2;154;205;50m[49m│[39m[44mSTYLE00 abcdefghij
|
||||||
[41mSTYLE01 abcdefghij [32m[49m│[39m[44mSTYLE01 abcdefghij
|
[41mSTYLE01 abcdefghij [38;2;154;205;50m[49m│[39m[44mSTYLE01 abcdefghij
|
||||||
[41mSTYLE02 abcdefghij [32m[49m│[39m[44mSTYLE02 abcdefghij
|
[41mSTYLE02 abcdefghij [38;2;154;205;50m[49m│[39m[44mSTYLE02 abcdefghij
|
||||||
[41mSTYLE03 abcdefghij [32m[49m│[39m[44mSTYLE03 abcdefghij
|
[41mSTYLE03 abcdefghij [38;2;154;205;50m[49m│[39m[44mSTYLE03 abcdefghij
|
||||||
[41mSTYLE04 abcdefghij [32m[49m│[39m[44mSTYLE04 abcdefghij
|
[41mSTYLE04 abcdefghij [38;2;154;205;50m[49m│[39m[44mSTYLE04 abcdefghij
|
||||||
[41mSTYLE05 abcdefghij [49m│[44mSTYLE05 abcdefghij
|
[41mSTYLE05 abcdefghij [38;2;179;179;179m[49m│[39m[44mSTYLE05 abcdefghij
|
||||||
[41mSTYLE06 abcdefghij [49m│[44mSTYLE06 abcdefghij
|
[41mSTYLE06 abcdefghij [38;2;179;179;179m[49m│[39m[44mSTYLE06 abcdefghij
|
||||||
[41m [49m│[44m
|
[41m [38;2;179;179;179m[49m│[39m[44m
|
||||||
|
|||||||
0
regress/session-group-resize.sh
Executable file → Normal file
0
regress/session-group-resize.sh
Executable file → Normal file
150
regress/targets-panes.sh
Normal file
150
regress/targets-panes.sh
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of pane target resolution in cmd-find.c.
|
||||||
|
#
|
||||||
|
# Building on targets.sh (session/window resolution), this exercises the pane
|
||||||
|
# half of cmd_find_target() in a known 2x2 split:
|
||||||
|
#
|
||||||
|
# - pane ids (%n), pane indices, and the +/- offset and ! last-pane tokens;
|
||||||
|
# - positional tokens {top-left}/{top-right}/{bottom-left}/{bottom-right}
|
||||||
|
# and {top}/{bottom}/{left}/{right};
|
||||||
|
# - directional tokens {up-of}/{down-of}/{left-of}/{right-of} relative to
|
||||||
|
# the active pane;
|
||||||
|
# - the ".pane" and "sess:win.pane" combined forms;
|
||||||
|
# - the marked pane, reached with ~ / {marked} and cleared with -M;
|
||||||
|
# - and the error paths: a pane id in the wrong window, a directional token
|
||||||
|
# with no neighbour, and an unset marked pane.
|
||||||
|
#
|
||||||
|
# The 2x2 split is created in a fixed order so pane ids are deterministic:
|
||||||
|
#
|
||||||
|
# +--------+--------+
|
||||||
|
# | %0 | %1 | top-left = %0 top-right = %1
|
||||||
|
# +--------+--------+ bottom-left = %2 bottom-right = %3
|
||||||
|
# | %2 | %3 |
|
||||||
|
# +--------+--------+
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# check $target $expected [format]
|
||||||
|
#
|
||||||
|
# The default format is the pane id.
|
||||||
|
check()
|
||||||
|
{
|
||||||
|
fmt=${3:-'#{pane_id}'}
|
||||||
|
out=$($TMUX display-message -p -t "$1" "$fmt" 2>&1)
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "target '$1' resolved wrong."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ok()
|
||||||
|
{
|
||||||
|
if ! $TMUX "$@"; then
|
||||||
|
echo "Command failed (expected success): $*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_fail()
|
||||||
|
{
|
||||||
|
out=$($TMUX has-session -t "$2" 2>&1)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "target '$2' resolved (expected failure)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$out" != "$1" ]; then
|
||||||
|
echo "Wrong error for target '$2'."
|
||||||
|
echo "Expected: '$1'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive 2>&1)" != "alive" ]; then
|
||||||
|
echo "Server died: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- fixture: a 2x2 split plus a single-pane window -----------------------
|
||||||
|
check_ok new-session -d -s p -x 80 -y 24
|
||||||
|
check_ok split-window -h -t p:0 # %0 left, %1 right
|
||||||
|
check_ok split-window -v -t p:0.%0 # split left: %0 top, %2 bottom
|
||||||
|
check_ok split-window -v -t p:0.%1 # split right: %1 top, %3 bottom
|
||||||
|
check_ok new-window -d -t p: -n solo # a second, single-pane window
|
||||||
|
|
||||||
|
# --- pane ids, index, offsets ---------------------------------------------
|
||||||
|
check "p:0.%3" "%3" # exact pane id
|
||||||
|
check "p:0.3" "%3" # pane by index
|
||||||
|
check "p:0.%1" "%1" # sess:win.pane form
|
||||||
|
check ".%1" "%1" # .pane form (current window)
|
||||||
|
# "sess:.pane" (empty window part) resolves the pane in the session's current
|
||||||
|
# window. Make window 0 current first.
|
||||||
|
check_ok select-window -t p:0
|
||||||
|
check "p:.%1" "%1"
|
||||||
|
check "p:.{top-left}" "%0"
|
||||||
|
# Offsets are relative to the active pane; make %0 active first.
|
||||||
|
check_ok select-pane -t p:0.%0
|
||||||
|
check "p:0.+" "1" '#{pane_index}' # next pane
|
||||||
|
check "p:0.-" "3" '#{pane_index}' # previous pane (wraps)
|
||||||
|
|
||||||
|
# --- last pane (!) --------------------------------------------------------
|
||||||
|
check_ok select-pane -t p:0.%2
|
||||||
|
check_ok select-pane -t p:0.%0 # now the last pane is %2
|
||||||
|
check "p:0.!" "%2"
|
||||||
|
|
||||||
|
# --- positional tokens (absolute geometry) --------------------------------
|
||||||
|
check "p:0.{top-left}" "%0"
|
||||||
|
check "p:0.{top-right}" "%1"
|
||||||
|
check "p:0.{bottom-left}" "%2"
|
||||||
|
check "p:0.{bottom-right}" "%3"
|
||||||
|
check "p:0.{top}" "%0" # leftmost of the top row
|
||||||
|
check "p:0.{bottom}" "%2" # leftmost of the bottom row
|
||||||
|
check "p:0.{left}" "%0" # top of the left column
|
||||||
|
check "p:0.{right}" "%1" # top of the right column
|
||||||
|
|
||||||
|
# --- directional tokens (relative to the active pane) ---------------------
|
||||||
|
#
|
||||||
|
# From the top-left pane the real neighbours are below and to the right.
|
||||||
|
check_ok select-pane -t p:0.%0
|
||||||
|
check "p:0.{down-of}" "%2"
|
||||||
|
check "p:0.{right-of}" "%1"
|
||||||
|
# From the bottom-right pane the real neighbours are above and to the left.
|
||||||
|
check_ok select-pane -t p:0.%3
|
||||||
|
check "p:0.{up-of}" "%1"
|
||||||
|
check "p:0.{left-of}" "%2"
|
||||||
|
|
||||||
|
# --- pane error paths -----------------------------------------------------
|
||||||
|
check_fail "can't find pane: %0" "p:solo.%0" # pane id, wrong window
|
||||||
|
check_fail "can't find pane: {up-of}" "p:solo.{up-of}" # no neighbour
|
||||||
|
check_fail "can't find pane: 9" "p:0.9" # no such index
|
||||||
|
|
||||||
|
# --- marked pane ----------------------------------------------------------
|
||||||
|
#
|
||||||
|
# ~ / {marked} resolve to the marked pane from anywhere; -M clears it.
|
||||||
|
check_fail "no marked target" "~" # nothing marked yet
|
||||||
|
check_ok select-pane -m -t p:0.%1
|
||||||
|
check "~" "%1"
|
||||||
|
check "{marked}" "%1"
|
||||||
|
# The mark is global: it resolves even with a different current window.
|
||||||
|
check_ok select-window -t p:solo
|
||||||
|
check "~" "%1"
|
||||||
|
check_ok select-window -t p:0
|
||||||
|
check_ok select-pane -M # clear the mark
|
||||||
|
check_fail "no marked target" "~"
|
||||||
|
check_fail "no marked target" "{marked}"
|
||||||
|
|
||||||
|
assert_alive "after pane target tests"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user