1use rmux_proto::TerminalSize;
2
3use super::{bool_value, window_raw_flags};
4use crate::{Pane, PaneId, Session, Window, WindowId};
5
6pub const DEFAULT_LIST_WINDOWS_FORMAT: &str =
8 "#{window_index}: #{window_name}#{window_raw_flags} (#{window_panes} panes) [#{window_width}x#{window_height}] [layout #{window_layout}] #{window_id}#{?window_active, (active),}";
9
10pub const DEFAULT_LIST_WINDOWS_ALL_FORMAT: &str =
12 "#{session_name}:#{window_index}: #{window_name}#{window_raw_flags} (#{window_panes} panes) [#{window_width}x#{window_height}] ";
13
14pub const DEFAULT_LIST_SESSIONS_FORMAT: &str =
16 "#{session_name}: #{session_windows} windows (created #{t:session_created})#{?session_grouped, (group #{session_group}),}#{?session_attached, (attached),}";
17
18pub const DEFAULT_LIST_PANES_WINDOW_FORMAT: &str =
20 "#{pane_index}: [#{pane_width}x#{pane_height}] [history #{history_size}/#{history_limit}, #{history_bytes} bytes] #{pane_id}#{?pane_active, (active),}#{?pane_dead, (dead),}";
21
22pub const DEFAULT_LIST_PANES_SESSION_FORMAT: &str =
24 "#{window_index}.#{pane_index}: [#{pane_width}x#{pane_height}] [history #{history_size}/#{history_limit}, #{history_bytes} bytes] #{pane_id}#{?pane_active, (active),}#{?pane_dead, (dead),}";
25
26pub const DEFAULT_LIST_PANES_ALL_FORMAT: &str =
28 "#{session_name}:#{window_index}.#{pane_index}: [#{pane_width}x#{pane_height}] [history #{history_size}/#{history_limit}, #{history_bytes} bytes] #{pane_id}#{?pane_active, (active),}#{?pane_dead, (dead),}";
29
30pub const DEFAULT_LIST_PANES_FORMAT: &str = DEFAULT_LIST_PANES_ALL_FORMAT;
32
33pub const DEFAULT_DISPLAY_MESSAGE_FORMAT: &str =
35 "[#{session_name}] #{window_index}:#{window_name}, current pane #{pane_index} - (%H:%M %d-%b-%y)";
36
37pub const TMUX_FORMAT_TABLE_NAMES: [&str; 192] = [
39 "active_window_index",
40 "alternate_on",
41 "alternate_saved_x",
42 "alternate_saved_y",
43 "bracket_paste_flag",
44 "buffer_created",
45 "buffer_full",
46 "buffer_mode_format",
47 "buffer_name",
48 "buffer_sample",
49 "buffer_size",
50 "client_activity",
51 "client_cell_height",
52 "client_cell_width",
53 "client_control_mode",
54 "client_created",
55 "client_discarded",
56 "client_flags",
57 "client_height",
58 "client_key_table",
59 "client_last_session",
60 "client_mode_format",
61 "client_name",
62 "client_pid",
63 "client_prefix",
64 "client_readonly",
65 "client_session",
66 "client_termfeatures",
67 "client_termname",
68 "client_termtype",
69 "client_theme",
70 "client_tty",
71 "client_uid",
72 "client_user",
73 "client_utf8",
74 "client_width",
75 "client_written",
76 "config_files",
77 "cursor_blinking",
78 "cursor_character",
79 "cursor_colour",
80 "cursor_flag",
81 "cursor_shape",
82 "cursor_very_visible",
83 "cursor_x",
84 "cursor_y",
85 "history_all_bytes",
86 "history_bytes",
87 "history_limit",
88 "history_size",
89 "host",
90 "host_short",
91 "insert_flag",
92 "keypad_cursor_flag",
93 "keypad_flag",
94 "last_window_index",
95 "loop_last_flag",
96 "mouse_all_flag",
97 "mouse_any_flag",
98 "mouse_button_flag",
99 "mouse_hyperlink",
100 "mouse_line",
101 "mouse_pane",
102 "mouse_sgr_flag",
103 "mouse_standard_flag",
104 "mouse_status_line",
105 "mouse_status_range",
106 "mouse_utf8_flag",
107 "mouse_word",
108 "mouse_x",
109 "mouse_y",
110 "next_session_id",
111 "origin_flag",
112 "pane_active",
113 "pane_at_bottom",
114 "pane_at_left",
115 "pane_at_right",
116 "pane_at_top",
117 "pane_bg",
118 "pane_bottom",
119 "pane_current_command",
120 "pane_current_path",
121 "pane_dead",
122 "pane_dead_signal",
123 "pane_dead_status",
124 "pane_dead_time",
125 "pane_fg",
126 "pane_flags",
127 "pane_floating_flag",
128 "pane_format",
129 "pane_height",
130 "pane_id",
131 "pane_in_mode",
132 "pane_index",
133 "pane_input_off",
134 "pane_key_mode",
135 "pane_last",
136 "pane_left",
137 "pane_marked",
138 "pane_marked_set",
139 "pane_mode",
140 "pane_path",
141 "pane_pb_progress",
142 "pane_pb_state",
143 "pane_pid",
144 "pane_pipe",
145 "pane_pipe_pid",
146 "pane_right",
147 "pane_search_string",
148 "pane_start_command",
149 "pane_start_path",
150 "pane_synchronized",
151 "pane_tabs",
152 "pane_title",
153 "pane_top",
154 "pane_tty",
155 "pane_unseen_changes",
156 "pane_width",
157 "pane_zoomed_flag",
158 "pid",
159 "scroll_region_lower",
160 "scroll_region_upper",
161 "server_sessions",
162 "session_active",
163 "session_activity",
164 "session_activity_flag",
165 "session_alert",
166 "session_alerts",
167 "session_attached",
168 "session_attached_list",
169 "session_bell_flag",
170 "session_created",
171 "session_format",
172 "session_group",
173 "session_group_attached",
174 "session_group_attached_list",
175 "session_group_list",
176 "session_group_many_attached",
177 "session_group_size",
178 "session_grouped",
179 "session_id",
180 "session_last_attached",
181 "session_many_attached",
182 "session_marked",
183 "session_name",
184 "session_path",
185 "session_silence_flag",
186 "session_stack",
187 "session_windows",
188 "sixel_support",
189 "socket_path",
190 "start_time",
191 "synchronized_output_flag",
192 "tree_mode_format",
193 "uid",
194 "user",
195 "version",
196 "window_active",
197 "window_active_clients",
198 "window_active_clients_list",
199 "window_active_sessions",
200 "window_active_sessions_list",
201 "window_activity",
202 "window_activity_flag",
203 "window_bell_flag",
204 "window_bigger",
205 "window_cell_height",
206 "window_cell_width",
207 "window_end_flag",
208 "window_flags",
209 "window_format",
210 "window_height",
211 "window_id",
212 "window_index",
213 "window_last_flag",
214 "window_layout",
215 "window_linked",
216 "window_linked_sessions",
217 "window_linked_sessions_list",
218 "window_marked_flag",
219 "window_name",
220 "window_offset_x",
221 "window_offset_y",
222 "window_panes",
223 "window_raw_flags",
224 "window_silence_flag",
225 "window_stack_index",
226 "window_start_flag",
227 "window_visible_layout",
228 "window_width",
229 "window_zoomed_flag",
230 "wrap_flag",
231];
232
233pub const TMUX_TIME_FORMAT_VARIABLE_NAMES: [&str; 9] = [
235 "buffer_created",
236 "client_activity",
237 "client_created",
238 "pane_dead_time",
239 "session_activity",
240 "session_created",
241 "session_last_attached",
242 "start_time",
243 "window_activity",
244];
245
246pub const FORMAT_VARIABLES: [FormatVariable; 20] = [
248 FormatVariable::SessionName,
249 FormatVariable::SessionWindows,
250 FormatVariable::SessionAttached,
251 FormatVariable::SessionWidth,
252 FormatVariable::SessionHeight,
253 FormatVariable::WindowIndex,
254 FormatVariable::WindowId,
255 FormatVariable::WindowName,
256 FormatVariable::WindowRawFlags,
257 FormatVariable::WindowPanes,
258 FormatVariable::WindowWidth,
259 FormatVariable::WindowHeight,
260 FormatVariable::WindowLayout,
261 FormatVariable::WindowActive,
262 FormatVariable::WindowLastFlag,
263 FormatVariable::PaneIndex,
264 FormatVariable::PaneId,
265 FormatVariable::PaneActive,
266 FormatVariable::PaneWidth,
267 FormatVariable::PaneHeight,
268];
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
272pub enum FormatVariable {
273 SessionName,
275 SessionWindows,
277 SessionAttached,
279 SessionWidth,
281 SessionHeight,
283 WindowIndex,
285 WindowId,
287 WindowName,
289 WindowRawFlags,
291 WindowPanes,
293 WindowWidth,
295 WindowHeight,
297 WindowLayout,
299 WindowActive,
301 WindowLastFlag,
303 PaneIndex,
305 PaneId,
307 PaneActive,
309 PaneWidth,
311 PaneHeight,
313}
314
315impl FormatVariable {
316 #[must_use]
318 pub fn from_name(name: &str) -> Option<Self> {
319 Some(match name {
320 "session_name" => Self::SessionName,
321 "session_windows" => Self::SessionWindows,
322 "session_attached" => Self::SessionAttached,
323 "session_width" => Self::SessionWidth,
324 "session_height" => Self::SessionHeight,
325 "window_index" => Self::WindowIndex,
326 "window_id" => Self::WindowId,
327 "window_name" => Self::WindowName,
328 "window_raw_flags" => Self::WindowRawFlags,
329 "window_panes" => Self::WindowPanes,
330 "window_width" => Self::WindowWidth,
331 "window_height" => Self::WindowHeight,
332 "window_layout" => Self::WindowLayout,
333 "window_active" => Self::WindowActive,
334 "window_last_flag" => Self::WindowLastFlag,
335 "pane_index" => Self::PaneIndex,
336 "pane_id" => Self::PaneId,
337 "pane_active" => Self::PaneActive,
338 "pane_width" => Self::PaneWidth,
339 "pane_height" => Self::PaneHeight,
340 _ => return None,
341 })
342 }
343
344 #[must_use]
346 pub const fn name(self) -> &'static str {
347 match self {
348 Self::SessionName => "session_name",
349 Self::SessionWindows => "session_windows",
350 Self::SessionAttached => "session_attached",
351 Self::SessionWidth => "session_width",
352 Self::SessionHeight => "session_height",
353 Self::WindowIndex => "window_index",
354 Self::WindowId => "window_id",
355 Self::WindowName => "window_name",
356 Self::WindowRawFlags => "window_raw_flags",
357 Self::WindowPanes => "window_panes",
358 Self::WindowWidth => "window_width",
359 Self::WindowHeight => "window_height",
360 Self::WindowLayout => "window_layout",
361 Self::WindowActive => "window_active",
362 Self::WindowLastFlag => "window_last_flag",
363 Self::PaneIndex => "pane_index",
364 Self::PaneId => "pane_id",
365 Self::PaneActive => "pane_active",
366 Self::PaneWidth => "pane_width",
367 Self::PaneHeight => "pane_height",
368 }
369 }
370}
371
372#[must_use]
374pub fn is_known_format_variable_name(name: &str) -> bool {
375 FormatVariable::from_name(name).is_some()
376 || TMUX_FORMAT_TABLE_NAMES.binary_search(&name).is_ok()
377}
378
379pub trait FormatVariables {
381 fn format_value(&self, variable: FormatVariable) -> Option<String>;
383
384 fn format_value_by_name(&self, name: &str) -> Option<String> {
390 FormatVariable::from_name(name).and_then(|v| self.format_value(v))
391 }
392
393 fn format_name_exists(&self, _scope: Option<char>, _name: &str) -> Option<bool> {
398 None
399 }
400
401 fn format_search(&self, _options: &str, _pattern: &str) -> Option<String> {
405 None
406 }
407
408 fn format_loop(
412 &self,
413 _scope: char,
414 _body: &str,
415 _current_body: Option<&str>,
416 _count_only: bool,
417 ) -> Option<String> {
418 None
419 }
420}
421
422#[derive(Debug, Clone, Default, PartialEq, Eq)]
424pub struct FormatContext {
425 session_name: Option<String>,
426 session_windows: Option<usize>,
427 session_attached: Option<usize>,
428 session_size: Option<TerminalSize>,
429 window_index: Option<u32>,
430 window_id: Option<WindowId>,
431 window_name: Option<String>,
432 window_panes: Option<usize>,
433 window_size: Option<TerminalSize>,
434 window_layout: Option<String>,
435 window_active: Option<bool>,
436 window_last_flag: Option<bool>,
437 pane_index: Option<u32>,
438 pane_id: Option<PaneId>,
439 pane_active: Option<bool>,
440 pane_size: Option<TerminalSize>,
441 named_values: Vec<(String, String)>,
442}
443
444impl FormatContext {
445 #[must_use]
447 pub fn new() -> Self {
448 Self::default()
449 }
450
451 #[must_use]
453 pub fn from_session(session: &Session) -> Self {
454 Self::new().with_session(session)
455 }
456
457 #[must_use]
459 pub fn with_session(mut self, session: &Session) -> Self {
460 self.session_name = Some(session.name().to_string());
461 self.session_windows = Some(session.windows().len());
462 self.session_size = Some(session.window().size());
463 self
464 }
465
466 #[must_use]
468 pub const fn with_session_attached(mut self, attached_count: usize) -> Self {
469 self.session_attached = Some(attached_count);
470 self
471 }
472
473 #[must_use]
475 pub fn with_window(
476 mut self,
477 window_index: u32,
478 window: &Window,
479 active: bool,
480 last: bool,
481 ) -> Self {
482 self.window_index = Some(window_index);
483 self.window_id = Some(window.id());
484 self.window_name = window.name().map(str::to_owned);
485 self.window_panes = Some(window.pane_count());
486 self.window_size = Some(window.size());
487 self.window_layout = Some(window.layout_dump());
488 self.window_active = Some(active);
489 self.window_last_flag = Some(last);
490 self
491 }
492
493 #[must_use]
495 pub fn with_pane(mut self, pane: &Pane, active: bool) -> Self {
496 let geometry = pane.geometry();
497 self.pane_index = Some(pane.index());
498 self.pane_id = Some(pane.id());
499 self.pane_active = Some(active);
500 self.pane_size = Some(TerminalSize {
501 cols: geometry.cols(),
502 rows: geometry.rows(),
503 });
504 self
505 }
506
507 #[must_use]
509 pub fn with_window_pane(self, window: &Window, pane: &Pane) -> Self {
510 self.with_pane(pane, pane.index() == window.active_pane_index())
511 }
512
513 #[must_use]
515 pub fn with_named_value(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
516 self.named_values.push((name.into(), value.into()));
517 self
518 }
519}
520
521impl FormatVariables for FormatContext {
522 fn format_value(&self, variable: FormatVariable) -> Option<String> {
523 match variable {
524 FormatVariable::SessionName => self.session_name.clone(),
525 FormatVariable::SessionWindows => self.session_windows.map(|value| value.to_string()),
526 FormatVariable::SessionAttached => self.session_attached.map(|value| value.to_string()),
527 FormatVariable::SessionWidth => self.session_size.map(|size| size.cols.to_string()),
528 FormatVariable::SessionHeight => self.session_size.map(|size| size.rows.to_string()),
529 FormatVariable::WindowIndex => self.window_index.map(|value| value.to_string()),
530 FormatVariable::WindowId => self.window_id.map(|value| value.to_string()),
531 FormatVariable::WindowName => self.window_name.clone(),
532 FormatVariable::WindowRawFlags => {
533 if self.window_active.is_some() || self.window_last_flag.is_some() {
534 Some(
535 window_raw_flags(
536 self.window_active.unwrap_or(false),
537 self.window_last_flag.unwrap_or(false),
538 )
539 .to_owned(),
540 )
541 } else {
542 None
543 }
544 }
545 FormatVariable::WindowPanes => self.window_panes.map(|value| value.to_string()),
546 FormatVariable::WindowWidth => self.window_size.map(|size| size.cols.to_string()),
547 FormatVariable::WindowHeight => self.window_size.map(|size| size.rows.to_string()),
548 FormatVariable::WindowLayout => self.window_layout.clone(),
549 FormatVariable::WindowActive => self.window_active.map(bool_value),
550 FormatVariable::WindowLastFlag => self.window_last_flag.map(bool_value),
551 FormatVariable::PaneIndex => self.pane_index.map(|value| value.to_string()),
552 FormatVariable::PaneId => self.pane_id.map(|value| value.to_string()),
553 FormatVariable::PaneActive => self.pane_active.map(bool_value),
554 FormatVariable::PaneWidth => self.pane_size.map(|size| size.cols.to_string()),
555 FormatVariable::PaneHeight => self.pane_size.map(|size| size.rows.to_string()),
556 }
557 }
558
559 fn format_value_by_name(&self, name: &str) -> Option<String> {
560 self.named_values
561 .iter()
562 .rev()
563 .find(|(candidate, _)| candidate == name)
564 .map(|(_, value)| value.clone())
565 .or_else(|| {
566 FormatVariable::from_name(name).and_then(|variable| self.format_value(variable))
567 })
568 }
569}