tmux 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +60 -0
- data/LICENSE +22 -0
- data/README.md +1076 -0
- data/bin/tmux.rb +6 -0
- data/bin/tmux_rb +4 -0
- data/lib/tmux/cli.rb +224 -0
- data/lib/tmux/command_builder.rb +104 -0
- data/lib/tmux/condition_compiler.rb +127 -0
- data/lib/tmux/config.rb +40 -0
- data/lib/tmux/config_builder.rb +247 -0
- data/lib/tmux/config_compiler.rb +82 -0
- data/lib/tmux/dsl.rb +118 -0
- data/lib/tmux/errors.rb +5 -0
- data/lib/tmux/escape.rb +68 -0
- data/lib/tmux/evaluation.rb +18 -0
- data/lib/tmux/expression.rb +109 -0
- data/lib/tmux/format.rb +94 -0
- data/lib/tmux/format_builder.rb +586 -0
- data/lib/tmux/format_compiler.rb +41 -0
- data/lib/tmux/glyph.rb +45 -0
- data/lib/tmux/option_tree.rb +148 -0
- data/lib/tmux/target.rb +200 -0
- data/lib/tmux/version.rb +5 -0
- data/lib/tmux/zone_builder.rb +31 -0
- data/lib/tmux.rb +47 -0
- metadata +92 -0
data/README.md
ADDED
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
# tmux.rb
|
|
2
|
+
|
|
3
|
+
tmux.rb is a Ruby DSL that compiles readable, structured block-based
|
|
4
|
+
configuration into native tmux configuration and format strings.
|
|
5
|
+
|
|
6
|
+
<!-- example: intro -->
|
|
7
|
+
```ruby
|
|
8
|
+
# ~/.config/tmux/tmux.rb
|
|
9
|
+
Tmux.configure do
|
|
10
|
+
global.prefix = "C-s" # set-option -g prefix "C-s"
|
|
11
|
+
global.mouse = true # set-option -g mouse on
|
|
12
|
+
window.mode_keys = :vi # set-option -gw mode-keys "vi"
|
|
13
|
+
|
|
14
|
+
global.status.right = Tmux.format do
|
|
15
|
+
if?(Tmux.window_zoomed_flag) do
|
|
16
|
+
left_flag "Z", bg: :white, fg: :black, attrs: :bold
|
|
17
|
+
end
|
|
18
|
+
left_segment Tmux.pane_current_command, bg: :colour238, fg: :white
|
|
19
|
+
left_segment "#{Tmux.session_name}@#{Tmux.host}", bg: :blue, fg: :black
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
bind "|", %q{split-window -h -c "#{pane_current_path}"}
|
|
23
|
+
end
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
tmux.rb build ~/.config/tmux/tmux.rb --output ~/.tmux.conf
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
<details>
|
|
31
|
+
<summary>Screenshot: the status line this configuration draws, in a zoomed window</summary>
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
</details>
|
|
36
|
+
|
|
37
|
+
Screenshots in this README are real tmux sessions rendering the example just
|
|
38
|
+
above them ([how they are made](docs/screenshots/README.md)). They use the
|
|
39
|
+
**Catppuccin Mocha** terminal color scheme and JetBrains Mono Nerd Font, with
|
|
40
|
+
the status bar set to the terminal's own colors. Named colors such as `:blue`,
|
|
41
|
+
`:white`, or `colour1` take their shade from your terminal's color scheme;
|
|
42
|
+
`colour16` to `colour255` and hex colors such as `"#ff8800"` look the same
|
|
43
|
+
everywhere. Powerline edges need a font with Powerline glyphs, such as a
|
|
44
|
+
[Nerd Font](https://www.nerdfonts.com).
|
|
45
|
+
|
|
46
|
+
## Why
|
|
47
|
+
|
|
48
|
+
Hand-written tmux configuration gets hard to change once it grows: status
|
|
49
|
+
lines become deeply nested `#{?...,...,...}` expressions where a misplaced
|
|
50
|
+
comma silently breaks rendering, and every level of quoting has to be escaped
|
|
51
|
+
by hand. tmux.rb keeps tmux's model but lets you write it as ordinary Ruby.
|
|
52
|
+
|
|
53
|
+
- **Compile, don't interpret.** Ruby runs only while you build or validate a
|
|
54
|
+
configuration. The output is plain tmux configuration, and tmux evaluates
|
|
55
|
+
its own formats, so rendering the status line never spawns Ruby and needs
|
|
56
|
+
nothing installed beyond tmux.
|
|
57
|
+
- **Skip manual escaping.** Write text, styles, variables, and conditionals
|
|
58
|
+
directly, without managing nested tmux quoting and escaping.
|
|
59
|
+
- **Keep tmux within reach.** The DSL adds readable names and helpers but
|
|
60
|
+
hides nothing: any option, command, or format string can still be written
|
|
61
|
+
directly, and `raw` passes tmux commands through verbatim.
|
|
62
|
+
- **Generate deterministic output.** The same source always produces
|
|
63
|
+
byte-identical configuration, so the generated file is easy to review and
|
|
64
|
+
compare in version control.
|
|
65
|
+
- **Treat the live server as a boundary.** `build`, `check`, and `diff` never
|
|
66
|
+
touch a running tmux server; only an explicit `load` does.
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
gem install tmux
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
tmux.rb needs Ruby 3.2 or newer. The full DSL is tested on tmux 3.2 and newer.
|
|
75
|
+
Compilation requires the target tmux executable to be installed.
|
|
76
|
+
|
|
77
|
+
The gem installs the `tmux.rb` command (also available as `tmux_rb`). Ruby
|
|
78
|
+
code that uses the library directly loads it with `require "tmux"`.
|
|
79
|
+
|
|
80
|
+
## Configuration files
|
|
81
|
+
|
|
82
|
+
`Tmux.configure` builds configuration commands: option setters, key bindings,
|
|
83
|
+
and other tmux commands. `Tmux.format` builds one reusable value, such as the
|
|
84
|
+
contents of `status-right`, which those commands can assign to an option.
|
|
85
|
+
|
|
86
|
+
A configuration file ends with either resulting object (`Tmux::Config` or
|
|
87
|
+
`Tmux::Format`). Run it with the `tmux.rb` command, or use
|
|
88
|
+
`Tmux.load_file("config.rb")` from Ruby. Constants and helper methods defined
|
|
89
|
+
at the top level stay local to that file.
|
|
90
|
+
|
|
91
|
+
**Configuration files are trusted code.** `build`, `check`, `diff`, and
|
|
92
|
+
`load` all execute them, with the same access as a Ruby script. Only build
|
|
93
|
+
configurations you would be willing to run as a script. A binding created
|
|
94
|
+
with `load_config` re-runs its Ruby file every time the key is pressed.
|
|
95
|
+
|
|
96
|
+
### Blocks and helper methods
|
|
97
|
+
|
|
98
|
+
Inside `Tmux.configure` and `Tmux.format` blocks, call DSL methods directly.
|
|
99
|
+
Methods you `def` and instance variables you set in a block stay visible in
|
|
100
|
+
its nested blocks, including conditional branches. Local variables and
|
|
101
|
+
lambdas work throughout the blocks too.
|
|
102
|
+
|
|
103
|
+
Give a block one parameter to keep Ruby's normal scoping, with the builder
|
|
104
|
+
passed in:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
def status_color = "colour37"
|
|
108
|
+
|
|
109
|
+
Tmux.configure do |c|
|
|
110
|
+
# self is unchanged here, so status_color is the method defined above.
|
|
111
|
+
c.global.status.style = "fg=#{status_color}" # set-option -g status-style "fg=colour37"
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## CLI
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
tmux.rb build config.rb # print the generated configuration
|
|
119
|
+
tmux.rb build config.rb --output tmux.conf # write it atomically
|
|
120
|
+
tmux.rb check config.rb # ask an isolated tmux server to parse it
|
|
121
|
+
tmux.rb diff config.rb ~/.tmux.conf # unified diff; exit 0 means identical
|
|
122
|
+
tmux.rb load config.rb # compile and source into a tmux server
|
|
123
|
+
tmux.rb load -S /tmp/outer.sock config.rb # ...into the server at a socket path
|
|
124
|
+
tmux.rb load -L work config.rb # ...or the server with a socket name
|
|
125
|
+
tmux.rb --version
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Every command accepts `--help`.
|
|
129
|
+
|
|
130
|
+
- **`build --output`** updates the destination atomically. A
|
|
131
|
+
symlinked destination (such as a `~/.tmux.conf` that points into a dotfiles
|
|
132
|
+
repository) is followed rather than replaced, and an existing file keeps
|
|
133
|
+
its permissions.
|
|
134
|
+
- **`check`** checks tmux syntax in an isolated server, including commands
|
|
135
|
+
inside `if?` branches. Option names and scopes are already checked
|
|
136
|
+
during compilation. It does not apply commands or validate option values.
|
|
137
|
+
Loading the file into an isolated server checks values too:
|
|
138
|
+
`tmux -L scratch -f /dev/null new-session -d 'sleep 600'`, then
|
|
139
|
+
`tmux -L scratch source-file tmux.conf`.
|
|
140
|
+
- **`load`** sources the result into the server `$TMUX` names, or into the
|
|
141
|
+
one given with `-S SOCKET` or `-L NAME`. Pass one of those from inside a
|
|
142
|
+
nested tmux, where `$TMUX` names the inner server.
|
|
143
|
+
|
|
144
|
+
Errors raised while evaluating a configuration are reported with the file and
|
|
145
|
+
line that caused them.
|
|
146
|
+
|
|
147
|
+
### Target tmux version
|
|
148
|
+
|
|
149
|
+
By default, configurations target the `tmux` on your `PATH`. All built-in
|
|
150
|
+
option names and scopes are checked against that executable's supported
|
|
151
|
+
options. To build for a different version, select its executable with
|
|
152
|
+
`--tmux`:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
tmux.rb build config.rb --tmux /opt/tmux-3.3/bin/tmux
|
|
156
|
+
tmux.rb check config.rb --tmux /opt/tmux-3.3/bin/tmux
|
|
157
|
+
tmux.rb load -S /tmp/outer.sock config.rb --tmux /opt/tmux-3.3/bin/tmux
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
From Ruby, use `Tmux.load_file("config.rb", tmux: "/opt/tmux-3.3/bin/tmux")`
|
|
161
|
+
or `Tmux.configure(tmux: "/opt/tmux-3.3/bin/tmux") { ... }`.
|
|
162
|
+
`load` refuses to apply a configuration if the destination server's version
|
|
163
|
+
does not match the selected executable. Use `tmux_from` and `tmux_below` for
|
|
164
|
+
settings that differ between versions; only the applicable version branch's
|
|
165
|
+
options are checked.
|
|
166
|
+
|
|
167
|
+
## Format DSL
|
|
168
|
+
|
|
169
|
+
Use `Tmux.format` for status lines, window tabs, pane borders, and anywhere
|
|
170
|
+
else tmux expands formats. [Assign the resulting format to an option](#format-valued-options)
|
|
171
|
+
to use it in a configuration:
|
|
172
|
+
|
|
173
|
+
<!-- example: window-tab -->
|
|
174
|
+
```ruby
|
|
175
|
+
# A window tab: the index in a colored block, then the name, which turns
|
|
176
|
+
# yellow while a flash is lit.
|
|
177
|
+
flash = Tmux.user_option(:@attn_flash)
|
|
178
|
+
phase = Tmux.user_option(:@attn_phase)
|
|
179
|
+
|
|
180
|
+
Tmux.format do
|
|
181
|
+
# Set the foreground and background for what follows.
|
|
182
|
+
style fg: :white, bg: :colour238
|
|
183
|
+
# Literal text with the window index interpolated; it stays dynamic.
|
|
184
|
+
text " #{Tmux.window_index} "
|
|
185
|
+
# The name's colors: black on yellow while flashing, grey otherwise.
|
|
186
|
+
if?(flash && phase) do
|
|
187
|
+
style fg: :black, bg: :yellow
|
|
188
|
+
end.else do
|
|
189
|
+
style fg: :colour250, bg: :colour236
|
|
190
|
+
end
|
|
191
|
+
# The window name, padded with a space on each side.
|
|
192
|
+
text " #{Tmux.window_name} "
|
|
193
|
+
# Return to the default style so nothing leaks into the next tab.
|
|
194
|
+
reset_style
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
<details>
|
|
199
|
+
<summary>Screenshot: three windows drawn with this format, the last one mid-flash</summary>
|
|
200
|
+
|
|
201
|
+

|
|
202
|
+
|
|
203
|
+
</details>
|
|
204
|
+
|
|
205
|
+
The sections below cover each part of the format DSL. Most calls can be
|
|
206
|
+
chained; `if?` supports `.elsif?` and `.else` branches.
|
|
207
|
+
|
|
208
|
+
### Text
|
|
209
|
+
|
|
210
|
+
`text` adds text that tmux shows as written. Expressions interpolated into the
|
|
211
|
+
string stay dynamic. Other characters, including `#`, `%`, `,`, and `}`,
|
|
212
|
+
print literally, even inside conditional branches.
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
Tmux.format do
|
|
216
|
+
# " 3 " for window 3: the interpolated index stays a tmux variable.
|
|
217
|
+
text " #{Tmux.window_index} "
|
|
218
|
+
# Expressions can also be passed as separate parts.
|
|
219
|
+
text "[", Tmux.session_name, "]"
|
|
220
|
+
# "#", "%", and "," print literally, even inside a conditional.
|
|
221
|
+
if?(Tmux.pane_active) { text "#1, 50%" }
|
|
222
|
+
# literal: true prints a string exactly as written, format code included.
|
|
223
|
+
text "\#{not_a_variable}", literal: true
|
|
224
|
+
end
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`text` rejects `#[` and `#(` in a string, since styles and shell commands
|
|
228
|
+
have their own methods (`style` and `command`).
|
|
229
|
+
|
|
230
|
+
### Dates and times
|
|
231
|
+
|
|
232
|
+
`strftime` adds a date/time format that tmux expands each time it renders.
|
|
233
|
+
Unlike `text`, its `%` codes are kept.
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
Tmux.format do
|
|
237
|
+
# "Tue 14:05", refreshed with the status line.
|
|
238
|
+
strftime "%a %H:%M"
|
|
239
|
+
end
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Appending expressions, formats, and format code
|
|
243
|
+
|
|
244
|
+
`append` adds tmux format source as is: an expression, a format built
|
|
245
|
+
elsewhere, or a string of tmux format code. Unlike `text`, a
|
|
246
|
+
string given to `append` is format code, not literal text.
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
clock = Tmux.format { strftime "%H:%M" }
|
|
250
|
+
|
|
251
|
+
Tmux.format do
|
|
252
|
+
# An expression.
|
|
253
|
+
append Tmux.pane_current_path
|
|
254
|
+
# Another format.
|
|
255
|
+
append clock
|
|
256
|
+
# Raw tmux format code, for anything the DSL has no helper for.
|
|
257
|
+
append "\#{t:window_activity}"
|
|
258
|
+
end
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Shell commands
|
|
262
|
+
|
|
263
|
+
`command` adds a `#(...)` shell command, which tmux runs in the background and
|
|
264
|
+
replaces with its output. Its text is format source, so `#{...}` codes expand
|
|
265
|
+
before the shell sees it. Commands must have balanced parentheses.
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
Tmux.format do
|
|
269
|
+
# The current git branch of the active pane's directory.
|
|
270
|
+
command "git -C \#{pane_current_path} branch --show-current"
|
|
271
|
+
end
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Styles
|
|
275
|
+
|
|
276
|
+
`style` sets colors and attributes for what follows; `reset_style` returns to
|
|
277
|
+
the default. Given a block, `style` applies only to the block's content,
|
|
278
|
+
which `p:` pads with spaces on both sides, and the style from before the
|
|
279
|
+
block returns after it.
|
|
280
|
+
|
|
281
|
+
<!-- example: styles -->
|
|
282
|
+
```ruby
|
|
283
|
+
Tmux.format do
|
|
284
|
+
# Colors and attributes as a hash.
|
|
285
|
+
style fg: "#000000", bg: "#ff0000", attrs: :bold
|
|
286
|
+
text " warning "
|
|
287
|
+
# Attribute names alone.
|
|
288
|
+
style :nobold
|
|
289
|
+
# A styled, padded block: the window index in white on blue.
|
|
290
|
+
style(fg: "#ffffff", bg: :blue, p: 1) do
|
|
291
|
+
append Tmux.window_index
|
|
292
|
+
end
|
|
293
|
+
# After the block, the black on red from before it returns.
|
|
294
|
+
text " here "
|
|
295
|
+
# Back to the default style.
|
|
296
|
+
reset_style
|
|
297
|
+
end
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
<details>
|
|
301
|
+
<summary>Screenshot: this format in the status line</summary>
|
|
302
|
+
|
|
303
|
+

|
|
304
|
+
|
|
305
|
+
</details>
|
|
306
|
+
|
|
307
|
+
### Conditionals
|
|
308
|
+
|
|
309
|
+
`if?` chooses content each time tmux renders. It includes its block when the
|
|
310
|
+
condition is true; use `!` to negate a condition.
|
|
311
|
+
Chain `.else` on `if?` for the false branch, or omit it to emit nothing when
|
|
312
|
+
false. Add `.elsif?(condition)` for more branches; the first matching branch
|
|
313
|
+
wins. Every branch can contain any format DSL calls, including nested
|
|
314
|
+
conditionals.
|
|
315
|
+
|
|
316
|
+
See [Expressions and conditions](#expressions-and-conditions) for variables,
|
|
317
|
+
comparisons, and combining tests with `&&`, `||`, and `!`. The same tests
|
|
318
|
+
work in configuration conditionals.
|
|
319
|
+
|
|
320
|
+
<!-- example: conditionals -->
|
|
321
|
+
```ruby
|
|
322
|
+
Tmux.format do
|
|
323
|
+
# Show "(name)" when the window is zoomed, the plain name otherwise.
|
|
324
|
+
if?(Tmux.window_zoomed_flag) do
|
|
325
|
+
text "(#{Tmux.window_name})"
|
|
326
|
+
end.else do
|
|
327
|
+
append Tmux.window_name
|
|
328
|
+
end
|
|
329
|
+
# Show a marker unless the pane is in a mode (copy mode, for example).
|
|
330
|
+
if?(!Tmux.pane_in_mode) { text " *" }
|
|
331
|
+
# Show " sync" first, otherwise " mode" in copy mode, or " async".
|
|
332
|
+
if?(Tmux.pane_synchronized) do
|
|
333
|
+
text " sync"
|
|
334
|
+
end.elsif?(Tmux.pane_in_mode) do
|
|
335
|
+
text " mode"
|
|
336
|
+
end.else do
|
|
337
|
+
text " async"
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
<details>
|
|
343
|
+
<summary>Screenshot: this format in a zoomed window</summary>
|
|
344
|
+
|
|
345
|
+

|
|
346
|
+
|
|
347
|
+
</details>
|
|
348
|
+
|
|
349
|
+
Outside a format block, `Tmux.if?` builds a reusable conditional format with
|
|
350
|
+
the same `.elsif?` and `.else` chain. Its branches use the format DSL; assign
|
|
351
|
+
the result to an option or `append` it inside another format:
|
|
352
|
+
|
|
353
|
+
```ruby
|
|
354
|
+
label = Tmux.if?(Tmux.pane_in_mode) do
|
|
355
|
+
text "copy"
|
|
356
|
+
end.elsif?(Tmux.client_prefix) do
|
|
357
|
+
text "prefix"
|
|
358
|
+
end.else do
|
|
359
|
+
text "normal"
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
Tmux.configure do
|
|
363
|
+
global.status.left = label
|
|
364
|
+
global.status.right = Tmux.format do
|
|
365
|
+
text "["
|
|
366
|
+
append label
|
|
367
|
+
text "]"
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Value and style switches
|
|
373
|
+
|
|
374
|
+
Use a [conditional chain](#conditionals) to choose which format DSL calls
|
|
375
|
+
run. Use `switch` to choose a value to pass to a call, such as a color for
|
|
376
|
+
`fg:` or `bg:`. The returned expression can be reused across styles or formats.
|
|
377
|
+
|
|
378
|
+
Call `switch` inside `Tmux.format` or `Tmux.configure` blocks, or
|
|
379
|
+
`Tmux.switch` outside them. Both calls build the same kind of expression;
|
|
380
|
+
choosing a value does not itself add content or configuration commands.
|
|
381
|
+
|
|
382
|
+
Both choose the first matching condition when tmux renders. A switch
|
|
383
|
+
requires an `else:` value; a conditional chain can omit `.else` to emit
|
|
384
|
+
nothing when no condition matches.
|
|
385
|
+
|
|
386
|
+
`switch_style` chooses a set of style attributes for the content that follows.
|
|
387
|
+
It takes the same mapping as `switch`, with style hashes as the choices, and
|
|
388
|
+
is shorthand for a conditional chain whose branches only call `style`.
|
|
389
|
+
|
|
390
|
+
<!-- example: switch -->
|
|
391
|
+
```ruby
|
|
392
|
+
Tmux.format do
|
|
393
|
+
# Orange while the prefix key is held, green in copy mode, grey otherwise.
|
|
394
|
+
background = switch(
|
|
395
|
+
Tmux.client_prefix => "#FF8800",
|
|
396
|
+
Tmux.pane_in_mode => "#6A9955",
|
|
397
|
+
else: "#A6ADC8"
|
|
398
|
+
)
|
|
399
|
+
# Use that color as a background.
|
|
400
|
+
style(fg: :black, bg: background, p: 1) { append Tmux.session_name }
|
|
401
|
+
# Back to the default style, then a space.
|
|
402
|
+
reset_style
|
|
403
|
+
text " "
|
|
404
|
+
# Choose an entire style: red when input is off, blue otherwise.
|
|
405
|
+
switch_style(Tmux.pane_input_off => {fg: :red}, else: {fg: :blue})
|
|
406
|
+
text "#{Tmux.pane_index}"
|
|
407
|
+
end
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
`switch` values can contain tmux format code. For literal text, use `text`
|
|
411
|
+
inside a conditional branch instead.
|
|
412
|
+
|
|
413
|
+
<details>
|
|
414
|
+
<summary>Screenshot: this format with pane input disabled, with the prefix held, and in copy mode</summary>
|
|
415
|
+
|
|
416
|
+

|
|
417
|
+
|
|
418
|
+

|
|
419
|
+
|
|
420
|
+

|
|
421
|
+
|
|
422
|
+
</details>
|
|
423
|
+
|
|
424
|
+
### Wrapping
|
|
425
|
+
|
|
426
|
+
`wrap` surrounds a block's content with a prefix and suffix. The suffix is
|
|
427
|
+
drawn in the style that was active before the block, even if the block
|
|
428
|
+
changed it, including a style chosen by `switch_style`.
|
|
429
|
+
|
|
430
|
+
<!-- example: wrap -->
|
|
431
|
+
```ruby
|
|
432
|
+
Tmux.format do
|
|
433
|
+
switch_style(Tmux.pane_input_off => {fg: :red}, else: {fg: :blue})
|
|
434
|
+
# Yellow "session:window.pane", with brackets in the selected color.
|
|
435
|
+
wrap "[", "]" do
|
|
436
|
+
style fg: :yellow
|
|
437
|
+
text "#{Tmux.session_name}:#{Tmux.window_index}."
|
|
438
|
+
# Highlight only the pane number while the prefix is held.
|
|
439
|
+
if?(Tmux.client_prefix) { style fg: :white }
|
|
440
|
+
append Tmux.pane_index
|
|
441
|
+
end
|
|
442
|
+
# A template lambda receives a placeholder for the green command name.
|
|
443
|
+
wrap ->(content) { " (#{content}) " } do
|
|
444
|
+
style fg: :green
|
|
445
|
+
append Tmux.pane_current_command
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
<details>
|
|
451
|
+
<summary>Screenshot: this format in the status line</summary>
|
|
452
|
+
|
|
453
|
+

|
|
454
|
+
|
|
455
|
+
</details>
|
|
456
|
+
|
|
457
|
+
### Powerline helpers
|
|
458
|
+
|
|
459
|
+
Flags, segments, and chips draw Powerline-style blocks with their edge glyphs
|
|
460
|
+
and color transitions. Their content can be a string (with interpolated
|
|
461
|
+
expressions), an expression, or an array mixing the two; a block draws custom
|
|
462
|
+
content instead.
|
|
463
|
+
|
|
464
|
+
- `left_segment` / `right_segment`: adjoining blocks with one pointed edge.
|
|
465
|
+
`left_segment` draws its edge over the previous block's background
|
|
466
|
+
automatically. For `right_segment`, set `over:` to the next block's
|
|
467
|
+
background to join the blocks without gaps.
|
|
468
|
+
- `left_flag` / `right_flag`: a self-contained badge, pointed at one end and
|
|
469
|
+
notched at the other. `over:` sets the background behind the point;
|
|
470
|
+
`notch_over:` can set a different background for the notch. If omitted,
|
|
471
|
+
the notch uses the same background as the point.
|
|
472
|
+
- `chip`: a floating block with an edge on each side. When both edges are thin,
|
|
473
|
+
`bg:` colors the outline and text, and `over:` sets the shared background
|
|
474
|
+
behind the edges and content (default `:default`). `fg:` sets the text color
|
|
475
|
+
for chips with solid edges.
|
|
476
|
+
- `p:` sets the padding spaces on each side (default 1); `attrs:` styles the
|
|
477
|
+
content.
|
|
478
|
+
- `left:` and `right:` choose a chip's edge shapes: `:triangle` (the default),
|
|
479
|
+
`:thin`, `:round`, `:round_thin`, `:rslant` (`/`), or `:lslant` (`\`).
|
|
480
|
+
The keyword supplies the side, so `left: :round, right: :round` makes a
|
|
481
|
+
rounded chip. Using the same slant on both sides makes a parallelogram;
|
|
482
|
+
the two slants can also be mixed.
|
|
483
|
+
- `glyph:` accepts the same shapes for flags and segments, using the helper's
|
|
484
|
+
`left_` or `right_` direction. Exact names from `Tmux::Glyph` and literal
|
|
485
|
+
glyph strings are also accepted when you want a specific character.
|
|
486
|
+
|
|
487
|
+
`left_` helpers point left, for the right side of the status line; `right_`
|
|
488
|
+
helpers point right, for the left side:
|
|
489
|
+
|
|
490
|
+
<!-- example: status-right -->
|
|
491
|
+
```ruby
|
|
492
|
+
# status-right: blocks chained from right to left.
|
|
493
|
+
Tmux.format do
|
|
494
|
+
# Path (with the home directory shortened to ~), then command; each edge
|
|
495
|
+
# is drawn over the previous block's color.
|
|
496
|
+
left_segment Tmux.pane_current_path.substitute("^#{Dir.home}", "~"), bg: :colour236, fg: :white
|
|
497
|
+
left_segment Tmux.pane_current_command, bg: :colour238, fg: :white
|
|
498
|
+
end
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
<details>
|
|
502
|
+
<summary>Screenshot: this format as status-right</summary>
|
|
503
|
+
|
|
504
|
+

|
|
505
|
+
|
|
506
|
+
</details>
|
|
507
|
+
|
|
508
|
+
<!-- example: status-left -->
|
|
509
|
+
```ruby
|
|
510
|
+
# status-left: blocks chained from left to right.
|
|
511
|
+
Tmux.format do
|
|
512
|
+
# Each edge sits over the next block's background for a continuous chain.
|
|
513
|
+
right_segment Tmux.session_name, bg: :blue, fg: :black, over: :colour238
|
|
514
|
+
# Content as a keyword, with its padding supplied by the string.
|
|
515
|
+
right_segment content: " #{Tmux.window_index} ", bg: :colour238, fg: :white, p: 0, over: :colour236
|
|
516
|
+
# The final edge meets the default background; only the text is italic.
|
|
517
|
+
right_flag(bg: :colour236, fg: :white, over: :default, attrs: :italics, close: false) do
|
|
518
|
+
text " #{Tmux.host_short} "
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
<details>
|
|
524
|
+
<summary>Screenshot: this format as status-left</summary>
|
|
525
|
+
|
|
526
|
+

|
|
527
|
+
|
|
528
|
+
</details>
|
|
529
|
+
|
|
530
|
+
<!-- example: chips -->
|
|
531
|
+
```ruby
|
|
532
|
+
Tmux.format do
|
|
533
|
+
# A chip with the default triangle edges...
|
|
534
|
+
chip "default", bg: :blue, fg: :black
|
|
535
|
+
text " "
|
|
536
|
+
# ...rounded edges...
|
|
537
|
+
chip "round", bg: :magenta, fg: :black, left: :round, right: :round
|
|
538
|
+
text " "
|
|
539
|
+
# ...thin edges and cyan text, all on the default background...
|
|
540
|
+
chip "thin", bg: :cyan, fg: :black, left: :thin, right: :thin
|
|
541
|
+
text " "
|
|
542
|
+
# ...and slants leaning right or left, with the side inferred for each edge.
|
|
543
|
+
chip "rslant", bg: :yellow, fg: :black, left: :rslant, right: :rslant
|
|
544
|
+
text " "
|
|
545
|
+
chip "lslant", bg: :green, fg: :black, left: :lslant, right: :lslant
|
|
546
|
+
end
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
<details>
|
|
550
|
+
<summary>Screenshot: these chips in the status line</summary>
|
|
551
|
+
|
|
552
|
+

|
|
553
|
+
|
|
554
|
+
</details>
|
|
555
|
+
|
|
556
|
+
### Zones
|
|
557
|
+
|
|
558
|
+
`Tmux.pane_border` builds a format from left, centre, and right zones, in
|
|
559
|
+
that order whatever order they are defined in. Each zone is an ordinary
|
|
560
|
+
format block.
|
|
561
|
+
|
|
562
|
+
<!-- example: zones -->
|
|
563
|
+
```ruby
|
|
564
|
+
Tmux.configure do
|
|
565
|
+
global.status = false
|
|
566
|
+
# Heavy pane borders with a label along the top, the active one in blue.
|
|
567
|
+
window.pane.border.status = :top
|
|
568
|
+
window.pane.border.lines = :heavy
|
|
569
|
+
window.pane.border.style = {fg: "#45475a"}
|
|
570
|
+
window.pane.active_border.style = {fg: "#89b4fa"}
|
|
571
|
+
|
|
572
|
+
active = Tmux.pane_active
|
|
573
|
+
accent = switch(active => "#89b4fa", else: "#45475a")
|
|
574
|
+
window.pane.border.format = Tmux.pane_border do
|
|
575
|
+
# The command and directory on the left, brighter on the active pane...
|
|
576
|
+
left do
|
|
577
|
+
style(fg: switch(active => "#cdd6f4", else: "#7f849c"), p: 1) do
|
|
578
|
+
text "#{Tmux.pane_current_command} #{Tmux.pane_current_path.substitute("^#{Dir.home}", "~")}"
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
# ...the window name in the middle...
|
|
582
|
+
center { append " #{Tmux.window_name} "}
|
|
583
|
+
# ...and the pane number as a chip on the right.
|
|
584
|
+
right { chip Tmux.pane_index, bg: accent, fg: "#11111b", left: :round, right: :round, p: 0 }
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
<details>
|
|
590
|
+
<summary>Screenshot: two panes with these borders</summary>
|
|
591
|
+
|
|
592
|
+

|
|
593
|
+
|
|
594
|
+
</details>
|
|
595
|
+
|
|
596
|
+
### Expanding options
|
|
597
|
+
|
|
598
|
+
`expand_option` inserts a user option's value and expands it as a format, for
|
|
599
|
+
segments that a plugin or script maintains. Spell user-option names with
|
|
600
|
+
tmux's `@` prefix: `:@my_plugin_segment` as a Ruby symbol, or
|
|
601
|
+
`"@my_plugin_segment"` as a string. The same spelling works for
|
|
602
|
+
`Tmux.user_option` expressions and scoped assignments such as
|
|
603
|
+
`global[:@my_plugin_segment] = "..."`.
|
|
604
|
+
|
|
605
|
+
```ruby
|
|
606
|
+
Tmux.format do
|
|
607
|
+
# Whatever format string a plugin keeps in @my_plugin_segment.
|
|
608
|
+
expand_option :@my_plugin_segment
|
|
609
|
+
end
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
## Expressions and conditions
|
|
613
|
+
|
|
614
|
+
Expressions represent tmux values that are evaluated when tmux uses them.
|
|
615
|
+
Use them as conditions in both configuration and format blocks, interpolate
|
|
616
|
+
them into `text`, or pass them to `append`, `style`, and the Powerline helpers.
|
|
617
|
+
|
|
618
|
+
```ruby
|
|
619
|
+
Tmux.format do
|
|
620
|
+
# Built-in variables: client_prefix, pane_active, pane_current_command,
|
|
621
|
+
# pane_current_path, pane_in_mode, pane_index, pane_input_off, pane_mode,
|
|
622
|
+
# pane_synchronized, window_index, window_name, window_zoomed_flag,
|
|
623
|
+
# session_name, host, host_short.
|
|
624
|
+
text "#{Tmux.session_name} "
|
|
625
|
+
# Any other tmux variable by name.
|
|
626
|
+
text "#{Tmux.variable(:window_panes)} "
|
|
627
|
+
# A user option (@attn_state).
|
|
628
|
+
if?(Tmux.user_option(:@attn_state)) { text "!" }
|
|
629
|
+
# Raw tmux format code as an expression.
|
|
630
|
+
text Tmux.raw("\#{t:session_created}")
|
|
631
|
+
end
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Use `>`, `<`, `>=`, and `<=` for numeric comparisons, and `==` / `!=` for
|
|
635
|
+
string comparisons. Put the expression on the left of the operator;
|
|
636
|
+
literal operands are escaped for you:
|
|
637
|
+
|
|
638
|
+
```ruby
|
|
639
|
+
command = Tmux.pane_current_command
|
|
640
|
+
|
|
641
|
+
Tmux.format do
|
|
642
|
+
# String comparison.
|
|
643
|
+
if?(command == "vim") { text "editing" }
|
|
644
|
+
if?(command != "zsh") { text "busy" }
|
|
645
|
+
|
|
646
|
+
# Numeric comparison.
|
|
647
|
+
if?(Tmux.pane_index > 9) { text "more than 9" }
|
|
648
|
+
if?(Tmux.pane_index < 2) { text "fewer than 2" }
|
|
649
|
+
if?(Tmux.pane_index >= 10) { text "10 or more" }
|
|
650
|
+
if?(Tmux.pane_index <= 1) { text "1 or fewer" }
|
|
651
|
+
|
|
652
|
+
# Regular-expression match.
|
|
653
|
+
if?(command.matches("^(bash|zsh|fish)$")) { text "shell" }
|
|
654
|
+
|
|
655
|
+
# Regular-expression substitution: shorten the home directory to "~/".
|
|
656
|
+
text Tmux.pane_current_path.substitute("^#{Dir.home}/", "~/")
|
|
657
|
+
end
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
`substitute` does not accept `:` in its pattern or replacement.
|
|
661
|
+
|
|
662
|
+
### Combining conditions
|
|
663
|
+
|
|
664
|
+
Write `&&` / `||` directly in `if?`, `.elsif?`, or `Tmux.if?` conditions, or
|
|
665
|
+
in inline `switch` / `switch_style` condition keys. Use `!` to negate an
|
|
666
|
+
expression. Comparisons bind more tightly than `&&` / `||`, and parentheses
|
|
667
|
+
work as in ordinary Ruby:
|
|
668
|
+
|
|
669
|
+
```ruby
|
|
670
|
+
active = Tmux.pane_active
|
|
671
|
+
|
|
672
|
+
Tmux.format do
|
|
673
|
+
if?(active && Tmux.client_prefix) { text "prefix" }
|
|
674
|
+
.elsif?(active && Tmux.pane_in_mode) { text "copy mode" }
|
|
675
|
+
.else { text "normal" }
|
|
676
|
+
|
|
677
|
+
style fg: switch(active && Tmux.client_prefix => :red, else: :white)
|
|
678
|
+
end
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
Use tmux expressions as logical operands, not Ruby booleans or `nil`. Keep
|
|
682
|
+
conditions to a single expression, without assignments, control flow, or
|
|
683
|
+
nested blocks. To reuse conditional content, build a `Tmux.if?` format.
|
|
684
|
+
|
|
685
|
+
`&&` / `||` require configuration files run through the CLI or `Tmux.load_file`,
|
|
686
|
+
not plain Ruby `require`, `load`, or direct library calls. Put combined
|
|
687
|
+
conditions directly in the calls above, not in intermediate assignments such
|
|
688
|
+
as `combined = active && Tmux.client_prefix`. Use `if?`, not Ruby's `if`, for
|
|
689
|
+
conditions that depend on tmux state.
|
|
690
|
+
|
|
691
|
+
## Configuration DSL
|
|
692
|
+
|
|
693
|
+
Inside `Tmux.configure`, statements apply in the order they are written.
|
|
694
|
+
|
|
695
|
+
### Options by name
|
|
696
|
+
|
|
697
|
+
`global` (session options), `window` (window and pane options), and `server`
|
|
698
|
+
support every built-in option available in the target tmux version, each in
|
|
699
|
+
its proper scope. Unknown names and settings in the wrong scope raise errors.
|
|
700
|
+
Use underscores for native dashes, group related names with dots, or use the
|
|
701
|
+
exact native name in brackets. These spellings are interchangeable and use
|
|
702
|
+
the same validation, as the equivalent assignments below show.
|
|
703
|
+
Assigning sets an option, `<<` appends to it, and `unset` reverts it to its
|
|
704
|
+
inherited value. Style options also accept a hash.
|
|
705
|
+
|
|
706
|
+
```ruby
|
|
707
|
+
Tmux.configure do
|
|
708
|
+
# Three equivalent ways to set status-interval.
|
|
709
|
+
global.status_interval = 5 # set-option -g status-interval 5
|
|
710
|
+
global.status.interval = 5 # set-option -g status-interval 5
|
|
711
|
+
global[:"status-interval"] = 5 # set-option -g status-interval 5
|
|
712
|
+
|
|
713
|
+
# Session options.
|
|
714
|
+
global.prefix = "C-s" # set-option -g prefix "C-s"
|
|
715
|
+
global.prefix2 = "C-a" # set-option -g prefix2 "C-a"
|
|
716
|
+
global.base_index = 1 # set-option -g base-index 1
|
|
717
|
+
global.status.position = :top # set-option -g status-position "top"
|
|
718
|
+
|
|
719
|
+
# A style option, written as a hash.
|
|
720
|
+
global.status.style = {bg: :default, fg: :white} # set-option -g status-style "bg=default,fg=white"
|
|
721
|
+
|
|
722
|
+
# Window and pane options.
|
|
723
|
+
window.mode_keys = :vi # set-option -gw mode-keys "vi"
|
|
724
|
+
|
|
725
|
+
# The same flexibility applies to pane-border-lines.
|
|
726
|
+
window.pane_border_lines = :heavy # set-option -gw pane-border-lines "heavy"
|
|
727
|
+
window.pane.border.lines = :heavy # set-option -gw pane-border-lines "heavy"
|
|
728
|
+
window[:"pane-border-lines"] = :heavy # set-option -gw pane-border-lines "heavy"
|
|
729
|
+
|
|
730
|
+
# Server options; << appends (set-option -a).
|
|
731
|
+
server.escape_time = 0 # set-option -s escape-time 0
|
|
732
|
+
server.terminal_features << ",*:RGB" # set-option -sa terminal-features ",*:RGB"
|
|
733
|
+
|
|
734
|
+
# Revert to the inherited value.
|
|
735
|
+
window.status.bell_style.unset # set-option -gwu window-status-bell-style
|
|
736
|
+
end
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### Native names, custom options, and arrays
|
|
740
|
+
|
|
741
|
+
Bracket notation accepts native option names with the same validation.
|
|
742
|
+
Use it for custom `@` options, whose names you or a plugin define. Integer
|
|
743
|
+
indices select elements of tmux array options.
|
|
744
|
+
|
|
745
|
+
```ruby
|
|
746
|
+
Tmux.configure do
|
|
747
|
+
# Brackets demonstrate an exact native name; global.status_interval = 5 is equivalent.
|
|
748
|
+
global[:"status-interval"] = 5 # set-option -g status-interval 5
|
|
749
|
+
window.monitor_bell = true # set-option -gw monitor-bell on
|
|
750
|
+
server.exit_empty = false # set-option -s exit-empty off
|
|
751
|
+
|
|
752
|
+
# Custom options use their native @ names, in whichever scope you need.
|
|
753
|
+
global[:@my_plugin_color] = "colour37" # set-option -g @my_plugin_color "colour37"
|
|
754
|
+
window[:@my_plugin_segment] = "!" # set-option -gw @my_plugin_segment "!"
|
|
755
|
+
|
|
756
|
+
# A custom terminal key sequence in an array option.
|
|
757
|
+
server.user_keys[0] = "\033[5;30012~" # set-option -s user-keys[0] "\033[5;30012~"
|
|
758
|
+
|
|
759
|
+
# Append to the current value.
|
|
760
|
+
global.update_environment << "MY_VAR" # set-option -ga update-environment "MY_VAR"
|
|
761
|
+
|
|
762
|
+
# Revert an option.
|
|
763
|
+
global.status.right.unset # set-option -gu status-right
|
|
764
|
+
end
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Format-valued options
|
|
768
|
+
|
|
769
|
+
Assign a `Tmux.format` wherever an option takes a format string:
|
|
770
|
+
|
|
771
|
+
```ruby
|
|
772
|
+
Tmux.configure do
|
|
773
|
+
# An option whose value is a format...
|
|
774
|
+
# set-option -g status-left " #{session_name} "
|
|
775
|
+
global.status.left = Tmux.format do
|
|
776
|
+
text " #{Tmux.session_name} "
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
# ...and a window option whose value is a format.
|
|
780
|
+
# set-option -gw window-status-format " #{window_index}:#{window_name} "
|
|
781
|
+
window.status.format = Tmux.format do
|
|
782
|
+
text " #{Tmux.window_index}:#{Tmux.window_name} "
|
|
783
|
+
end
|
|
784
|
+
end
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Variable expansion
|
|
788
|
+
|
|
789
|
+
When tmux loads the configuration it expands `$NAME` (a name starting with a
|
|
790
|
+
letter or `_`) and a leading `~` in option values, just as in hand-written
|
|
791
|
+
configuration. `expand: false` keeps a value literal until a shell or a
|
|
792
|
+
`#(...)` command sees it. Use an option's `set` method to pass `expand: false`
|
|
793
|
+
or `append: true` alongside its value.
|
|
794
|
+
|
|
795
|
+
```ruby
|
|
796
|
+
Tmux.configure do
|
|
797
|
+
# tmux stores your home directory here, expanded when the file loads.
|
|
798
|
+
global[:@home] = "$HOME" # set-option -g @home "$HOME"
|
|
799
|
+
|
|
800
|
+
# Stored literally, as "~/bin/$COMMAND".
|
|
801
|
+
global[:@literal_path].set("~/bin/$COMMAND", expand: false) # set-option -g @literal_path "\~/bin/\$COMMAND"
|
|
802
|
+
|
|
803
|
+
# The shell, not tmux, expands $HOME each time the command runs.
|
|
804
|
+
# set-option -g status-right "#([ -f \$HOME/.name ] && cat \$HOME/.name || hostname)"
|
|
805
|
+
global.status.right.set(Tmux.format do
|
|
806
|
+
command '[ -f $HOME/.name ] && cat $HOME/.name || hostname'
|
|
807
|
+
end, expand: false)
|
|
808
|
+
end
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
### Environment, sourcing, and shell commands
|
|
812
|
+
|
|
813
|
+
```ruby
|
|
814
|
+
Tmux.configure do
|
|
815
|
+
# A global environment variable for new panes.
|
|
816
|
+
environment "EDITOR", "nvim"
|
|
817
|
+
|
|
818
|
+
# Load another tmux file; quiet: true skips a missing one without an error.
|
|
819
|
+
source "~/.tmux.local.conf", quiet: true
|
|
820
|
+
|
|
821
|
+
# Run a shell command while the file loads (background: true for -b).
|
|
822
|
+
run_shell "~/.tmux/plugins/tpm/tpm", background: true
|
|
823
|
+
end
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### Key bindings
|
|
827
|
+
|
|
828
|
+
`bind` maps a key to a tmux command string or a block of commands. Use the
|
|
829
|
+
block form to pass command arguments without quoting them by hand.
|
|
830
|
+
`table:` picks a key table, `no_prefix: true` binds without the prefix (`-n`),
|
|
831
|
+
`repeat: true` allows repeating (`-r`), and `note:` adds a description (`-N`).
|
|
832
|
+
`unbind` removes a binding.
|
|
833
|
+
|
|
834
|
+
```ruby
|
|
835
|
+
Tmux.configure do
|
|
836
|
+
# A command string.
|
|
837
|
+
# tmux: bind-key "|" split-window -h -c "#{pane_current_path}"
|
|
838
|
+
bind "|", %q{split-window -h -c "#{pane_current_path}"}
|
|
839
|
+
|
|
840
|
+
# Repeatable, so prefix + < < < resizes three times.
|
|
841
|
+
# tmux: bind-key -r "<" resize-pane -L 10
|
|
842
|
+
bind "<", "resize-pane -L 10", repeat: true
|
|
843
|
+
|
|
844
|
+
# Without the prefix, with a description for list-keys -N.
|
|
845
|
+
# tmux: bind-key -n -N "Zoom the pane" "M-z" resize-pane -Z
|
|
846
|
+
bind "M-z", "resize-pane -Z", no_prefix: true, note: "Zoom the pane"
|
|
847
|
+
|
|
848
|
+
# A block in the copy-mode-vi key table.
|
|
849
|
+
# tmux: bind-key -T "copy-mode-vi" "WheelUpPane" select-pane \; send-keys -t "{mouse}" -X clear-selection \; send-keys -t "{mouse}" -X -N 5 scroll-up
|
|
850
|
+
bind "WheelUpPane", table: "copy-mode-vi" do
|
|
851
|
+
# Select the pane under the mouse...
|
|
852
|
+
select_pane
|
|
853
|
+
|
|
854
|
+
# ...then send copy-mode commands to it.
|
|
855
|
+
copy_mode :clear_selection, target: "{mouse}"
|
|
856
|
+
copy_mode :scroll_up, target: "{mouse}", count: 5
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
# Remove the default prefix binding, a root-table binding, and a
|
|
860
|
+
# copy-mode one.
|
|
861
|
+
# tmux: unbind-key "C-b"
|
|
862
|
+
unbind "C-b"
|
|
863
|
+
|
|
864
|
+
# tmux: unbind-key -n "M-z"
|
|
865
|
+
unbind "M-z", no_prefix: true
|
|
866
|
+
|
|
867
|
+
# tmux: unbind-key -T "copy-mode-vi" "v"
|
|
868
|
+
unbind "v", table: "copy-mode-vi"
|
|
869
|
+
end
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
A binding block offers `command` for any tmux command (its arguments are
|
|
873
|
+
quoted for you), `select_pane`, `copy_mode`, `display_message`,
|
|
874
|
+
`display_menu`, `load_config`, and `raw`:
|
|
875
|
+
|
|
876
|
+
```ruby
|
|
877
|
+
Tmux.configure do
|
|
878
|
+
# tmux: bind-key "m" display-menu -T "Layout" "Tiled" "t" "select-layout tiled" "" "Even horizontal" "h" "select-layout \"even-horizontal\" \\; display-message \"Layout: even horizontal\""
|
|
879
|
+
bind "m" do
|
|
880
|
+
# A menu titled "Layout"; each item runs a command or a command block.
|
|
881
|
+
display_menu title: "Layout" do
|
|
882
|
+
item "Tiled", key: "t", command: "select-layout tiled"
|
|
883
|
+
separator
|
|
884
|
+
item "Even horizontal", key: "h" do
|
|
885
|
+
command :select_layout, "even-horizontal"
|
|
886
|
+
display_message "Layout: even horizontal"
|
|
887
|
+
end
|
|
888
|
+
end
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
# Any command, with quoted arguments; select_pane can target a pane.
|
|
892
|
+
# tmux: bind-key "N" new-window "-c" "#{pane_current_path}" \; select-pane -t "{last}"
|
|
893
|
+
bind "N" do
|
|
894
|
+
command :new_window, "-c", "\#{pane_current_path}"
|
|
895
|
+
select_pane target: "{last}"
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
# raw adds a command exactly as written.
|
|
899
|
+
# tmux: bind-key "X" confirm-before -p 'kill window? (y/n)' kill-window
|
|
900
|
+
bind "X" do
|
|
901
|
+
raw "confirm-before -p 'kill window? (y/n)' kill-window"
|
|
902
|
+
end
|
|
903
|
+
end
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
`commands` builds a command sequence outside a binding, so helper methods can
|
|
907
|
+
return one and several bindings can share it:
|
|
908
|
+
|
|
909
|
+
```ruby
|
|
910
|
+
Tmux.configure do
|
|
911
|
+
def split_here(direction)
|
|
912
|
+
commands do
|
|
913
|
+
command :split_window, direction, "-c", "\#{pane_current_path}"
|
|
914
|
+
display_message "split"
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
# tmux: bind-key "|" split-window "-h" -c "#{pane_current_path}" \; display-message "split"
|
|
919
|
+
bind "|", split_here("-h")
|
|
920
|
+
|
|
921
|
+
# tmux: bind-key "-" split-window "-v" -c "#{pane_current_path}" \; display-message "split"
|
|
922
|
+
bind "-", split_here("-v")
|
|
923
|
+
end
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### Reloading from Ruby
|
|
927
|
+
|
|
928
|
+
`load_config` returns commands that recompile a Ruby configuration and load
|
|
929
|
+
it, for use with `bind`. The binding passes the running server's socket, so
|
|
930
|
+
it reloads the server whose key was pressed.
|
|
931
|
+
|
|
932
|
+
```ruby
|
|
933
|
+
Tmux.configure do
|
|
934
|
+
# A helper method, reusable in this configuration.
|
|
935
|
+
def reload_config
|
|
936
|
+
load_config __FILE__, message: "tmux reloaded"
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
# prefix + r recompiles this file and loads it.
|
|
940
|
+
bind "r", reload_config
|
|
941
|
+
|
|
942
|
+
# executable: names the tmux.rb to run, e.g. one outside $PATH.
|
|
943
|
+
bind "R", load_config(__FILE__, executable: File.expand_path("~/bin/tmux.rb"))
|
|
944
|
+
end
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
### Conditional configuration
|
|
948
|
+
|
|
949
|
+
Use the same [conditional chain](#conditionals) as in format blocks to choose
|
|
950
|
+
which configuration commands tmux runs. Pass a [tmux expression](#expressions-and-conditions) directly, or
|
|
951
|
+
use `shell("command")` to check whether a shell command exits successfully.
|
|
952
|
+
Shell checks run when tmux applies the configuration. Later branches are
|
|
953
|
+
checked only if the earlier ones fail. Use `Tmux.shell(...)` to define a
|
|
954
|
+
reusable check outside a configuration block.
|
|
955
|
+
|
|
956
|
+
Use `shell("command", background: true)` to let tmux continue without waiting
|
|
957
|
+
for the check. For an existing native format string, use `Tmux.raw('...')`
|
|
958
|
+
as the condition; `if?(expression, background: true)` also supports tmux's
|
|
959
|
+
background flag. Shell checks can be used in configuration conditionals;
|
|
960
|
+
format blocks use tmux expressions.
|
|
961
|
+
|
|
962
|
+
`tmux_from` and `tmux_below` apply configuration on tmux at least, or older
|
|
963
|
+
than, a `"major.minor"` version, based on the running server's version.
|
|
964
|
+
|
|
965
|
+
```ruby
|
|
966
|
+
Tmux.configure do
|
|
967
|
+
# Choose the first available clipboard tool.
|
|
968
|
+
if?(shell("command -v pbcopy")) do
|
|
969
|
+
global[:@yank_command] = "pbcopy" # set-option -g @yank_command "pbcopy"
|
|
970
|
+
end.elsif?(shell("command -v wl-copy")) do
|
|
971
|
+
global[:@yank_command] = "wl-copy" # set-option -g @yank_command "wl-copy"
|
|
972
|
+
end.else do
|
|
973
|
+
global[:@yank_command] = "xclip -selection clipboard" # set-option -g @yank_command "xclip -selection clipboard"
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
# Choose a style based on the shell environment.
|
|
977
|
+
if?(shell('test -n "$SSH_CONNECTION"')) do
|
|
978
|
+
global.status.style = "bg=colour52" # set-option -g status-style "bg=colour52"
|
|
979
|
+
end.else do
|
|
980
|
+
global.status.style = "bg=colour236" # set-option -g status-style "bg=colour236"
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
# A format condition instead of a shell command.
|
|
984
|
+
if?(Tmux.host_short == "laptop") do
|
|
985
|
+
global.status.style = "bg=colour22" # set-option -g status-style "bg=colour22"
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
# Don't wait for a slow check while tmux starts.
|
|
989
|
+
if?(shell("command -v slow-check", background: true)) do
|
|
990
|
+
global[:@slow_check] = "yes" # set-option -g @slow_check "yes"
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
# Only on tmux 3.2 or newer...
|
|
994
|
+
tmux_from "3.2" do
|
|
995
|
+
server.extended_keys = :on # set-option -s extended-keys "on"
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
# ...and a fallback for older versions, with .else for the rest.
|
|
999
|
+
tmux_below "3.0" do
|
|
1000
|
+
global.status.style = "bg=black" # set-option -g status-style "bg=black"
|
|
1001
|
+
end.else do
|
|
1002
|
+
global.status.style = "bg=terminal" # set-option -g status-style "bg=terminal"
|
|
1003
|
+
end
|
|
1004
|
+
end
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
### Raw commands and composition
|
|
1008
|
+
|
|
1009
|
+
`raw` passes tmux commands through verbatim (one per line; a trailing `\`
|
|
1010
|
+
continues a line), and `blank` adds an empty line. `append` includes another
|
|
1011
|
+
configuration's statements, and `use` accepts any object that responds to
|
|
1012
|
+
`to_config`, such as a plugin's Ruby package.
|
|
1013
|
+
|
|
1014
|
+
```ruby
|
|
1015
|
+
shared = Tmux.configure { global.history_limit = 50_000 } # set-option -g history-limit 50000
|
|
1016
|
+
|
|
1017
|
+
# Anything with a to_config method can be used.
|
|
1018
|
+
clipboard = Object.new
|
|
1019
|
+
def clipboard.to_config = Tmux.configure { server.set_clipboard = :external } # set-option -s set-clipboard "external"
|
|
1020
|
+
|
|
1021
|
+
Tmux.configure do
|
|
1022
|
+
# Verbatim tmux commands, including a continued line.
|
|
1023
|
+
raw "set-option -g display-time 2000"
|
|
1024
|
+
raw "bind-key T \\\n clock-mode"
|
|
1025
|
+
blank
|
|
1026
|
+
|
|
1027
|
+
# Another configuration's statements, in place.
|
|
1028
|
+
append shared
|
|
1029
|
+
use clipboard
|
|
1030
|
+
end
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
## Examples
|
|
1034
|
+
|
|
1035
|
+
- [examples/author_config.rb](https://github.com/cosmicbuffalo/tmux.rb/blob/main/examples/author_config.rb)
|
|
1036
|
+
is the author's complete tmux configuration: structured status bars, pane
|
|
1037
|
+
borders, and window tabs, plus integration with
|
|
1038
|
+
[tmux-ai-attn](https://github.com/cosmicbuffalo/tmux-ai-attn).
|
|
1039
|
+
- [examples/popular_configs](https://github.com/cosmicbuffalo/tmux.rb/blob/main/examples/popular_configs/README.md)
|
|
1040
|
+
contains ten ports of well-known public tmux configurations. Each pinned
|
|
1041
|
+
upstream config sits beside its Ruby version for comparison.
|
|
1042
|
+
- [examples/window_status.rb](https://github.com/cosmicbuffalo/tmux.rb/blob/main/examples/window_status.rb)
|
|
1043
|
+
is a standalone format file.
|
|
1044
|
+
|
|
1045
|
+
## Development
|
|
1046
|
+
|
|
1047
|
+
```bash
|
|
1048
|
+
bundle install
|
|
1049
|
+
bundle exec rake test
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
`bundle exec rake test` uses the tmux on your `PATH`.
|
|
1053
|
+
`bundle exec rake test:tmux` builds each tmux version CI tests (3.2a, the
|
|
1054
|
+
newest release of each later minor version, and the development branch)
|
|
1055
|
+
from source into `tmp/tmux` and runs the suite against each; set
|
|
1056
|
+
`VERSIONS="3.2a 3.8"` to pick some. Building needs a C compiler, bison,
|
|
1057
|
+
pkg-config, and the libevent and ncurses headers.
|
|
1058
|
+
|
|
1059
|
+
The suite starts throwaway tmux servers on sockets in temporary directories
|
|
1060
|
+
and never touches your running server. Tests that need tmux are skipped when
|
|
1061
|
+
it is not installed, except on CI, where they fail. After an intended change
|
|
1062
|
+
to the author config's output, regenerate its fixture with
|
|
1063
|
+
`UPDATE_FIXTURES=1 bundle exec rake test` and review the diff.
|
|
1064
|
+
|
|
1065
|
+
The README screenshots are regenerated from real tmux sessions with
|
|
1066
|
+
`bundle exec rake screenshots`; see
|
|
1067
|
+
[docs/screenshots](docs/screenshots/README.md).
|
|
1068
|
+
|
|
1069
|
+
Build the gem with `bundle exec rake build` (into `pkg/`), and publish with
|
|
1070
|
+
`bundle exec rake release`.
|
|
1071
|
+
|
|
1072
|
+
## License
|
|
1073
|
+
|
|
1074
|
+
MIT. See [LICENSE](https://github.com/cosmicbuffalo/tmux.rb/blob/main/LICENSE).
|
|
1075
|
+
The ported configurations in `examples/popular_configs` keep their upstream
|
|
1076
|
+
licenses beside them.
|