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