1use crate::cli::Command;
3use crate::data::{InputMode, ThemeHue, WebSharing};
4use clap::{Args, ValueEnum};
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7use std::str::FromStr;
8
9use std::net::IpAddr;
10
11pub const DEFAULT_WORD_SEPARATORS: &str = "[]{}<>()";
12
13#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ValueEnum)]
14pub enum OnForceClose {
15 #[serde(alias = "quit")]
16 Quit,
17 #[serde(alias = "detach")]
18 Detach,
19}
20
21#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ValueEnum)]
22pub enum NestedSessionHandling {
23 #[serde(alias = "ask")]
24 Ask,
25 #[serde(alias = "fullscreen")]
26 Fullscreen,
27 #[serde(alias = "descend")]
28 Descend,
29 #[serde(alias = "never")]
30 Never,
31}
32
33impl Default for NestedSessionHandling {
34 fn default() -> Self {
35 Self::Ask
36 }
37}
38
39impl FromStr for NestedSessionHandling {
40 type Err = String;
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 match s {
43 "Ask" | "ask" => Ok(Self::Ask),
44 "Fullscreen" | "fullscreen" => Ok(Self::Fullscreen),
45 "Descend" | "descend" => Ok(Self::Descend),
46 "Never" | "never" => Ok(Self::Never),
47 _ => Err(format!("No such nested_session_handling: {}", s)),
48 }
49 }
50}
51
52impl Default for OnForceClose {
53 fn default() -> Self {
54 Self::Detach
55 }
56}
57
58impl FromStr for OnForceClose {
59 type Err = Box<dyn std::error::Error>;
60
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 match s {
63 "quit" => Ok(Self::Quit),
64 "detach" => Ok(Self::Detach),
65 e => Err(e.to_string().into()),
66 }
67 }
68}
69
70#[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
71#[serde(rename_all = "lowercase")]
72pub enum PaneFrameStyle {
73 Full,
74 Titles,
75 None,
76}
77
78impl Default for PaneFrameStyle {
79 fn default() -> Self {
80 PaneFrameStyle::Titles
81 }
82}
83
84impl PaneFrameStyle {
85 pub fn draws_full_frames(&self) -> bool {
86 matches!(self, PaneFrameStyle::Full)
87 }
88
89 pub fn draws_titles(&self) -> bool {
90 matches!(self, PaneFrameStyle::Titles)
91 }
92
93 pub fn from_options(options: &Options) -> Self {
94 if options.pane_frames == Some(false) {
95 return PaneFrameStyle::None;
96 }
97 match options.pane_frame_style {
98 Some(PaneFrameStyle::Full) => PaneFrameStyle::Full,
99 _ => PaneFrameStyle::Titles,
100 }
101 }
102}
103
104impl FromStr for PaneFrameStyle {
105 type Err = Box<dyn std::error::Error>;
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 match s.trim().to_lowercase().as_str() {
108 "full" => Ok(PaneFrameStyle::Full),
109 "titles" => Ok(PaneFrameStyle::Titles),
110 "none" => Ok(PaneFrameStyle::None),
111 e => Err(format!(
112 "Unknown pane frame style: '{}' (expected 'full', 'titles' or 'none')",
113 e
114 )
115 .into()),
116 }
117 }
118}
119
120#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize, Args)]
121pub struct Options {
126 #[clap(long, value_parser)]
129 #[serde(default)]
130 pub simplified_ui: Option<bool>,
131 #[clap(long, value_parser)]
133 pub theme: Option<String>,
134 #[clap(long, value_parser)]
138 pub theme_dark: Option<String>,
139 #[clap(long, value_parser)]
143 pub theme_light: Option<String>,
144 #[clap(long, value_enum, hide_possible_values = true, value_parser)]
149 pub explicit_theme_hue: Option<ThemeHue>,
150 #[clap(long, value_enum, hide_possible_values = true, value_parser)]
152 pub default_mode: Option<InputMode>,
153 #[clap(long, value_parser)]
155 pub default_shell: Option<PathBuf>,
156 #[clap(long, value_parser)]
158 pub default_cwd: Option<PathBuf>,
159 #[clap(long, value_parser)]
161 pub default_layout: Option<PathBuf>,
162 #[clap(long, value_parser)]
165 pub layout_dir: Option<PathBuf>,
166 #[clap(long, value_parser)]
169 pub theme_dir: Option<PathBuf>,
170 #[clap(long, value_parser)]
171 #[serde(default)]
172 pub mouse_mode: Option<bool>,
175 #[clap(long, value_parser)]
176 #[serde(default)]
177 pub pane_frames: Option<bool>,
179 #[clap(long, value_enum, hide_possible_values = true, value_parser)]
180 #[serde(default)]
181 pub pane_frame_style: Option<PaneFrameStyle>,
182 #[clap(long, value_parser)]
183 #[serde(default)]
184 pub mirror_session: Option<bool>,
186 #[clap(long, value_enum, hide_possible_values = true, value_parser)]
188 pub on_force_close: Option<OnForceClose>,
189 #[clap(long, value_parser)]
190 pub scroll_buffer_size: Option<usize>,
191
192 #[clap(long, value_parser)]
194 #[serde(default)]
195 pub copy_command: Option<String>,
196
197 #[clap(
199 long,
200 value_enum,
201 ignore_case = true,
202 conflicts_with = "copy_command",
203 value_parser
204 )]
205 #[serde(default)]
206 pub copy_clipboard: Option<Clipboard>,
207
208 #[clap(long, value_parser)]
210 #[serde(default)]
211 pub copy_on_select: Option<bool>,
212
213 #[clap(long, value_parser)]
215 #[serde(default)]
216 pub osc8_hyperlinks: Option<bool>,
217
218 #[clap(long, value_parser)]
220 pub scrollback_editor: Option<PathBuf>,
221
222 #[clap(long, value_parser)]
224 #[serde(default)]
225 pub session_name: Option<String>,
226
227 #[clap(long, value_parser)]
229 #[serde(default)]
230 pub attach_to_session: Option<bool>,
231
232 #[clap(long, value_parser)]
234 #[serde(default)]
235 pub auto_layout: Option<bool>,
236
237 #[clap(long, value_parser)]
240 #[serde(default)]
241 pub session_serialization: Option<bool>,
242
243 #[clap(long, value_parser)]
245 #[serde(default)]
246 pub serialize_pane_viewport: Option<bool>,
247
248 #[clap(long, value_parser)]
252 #[serde(default)]
253 pub scrollback_lines_to_serialize: Option<usize>,
254
255 #[clap(long, value_parser)]
257 #[serde(default)]
258 pub styled_underlines: Option<bool>,
259
260 #[clap(long, value_parser)]
262 pub serialization_interval: Option<u64>,
263
264 #[clap(long, value_parser)]
266 pub disable_session_metadata: Option<bool>,
267
268 #[clap(long, value_parser)]
271 #[serde(default)]
272 pub support_kitty_keyboard_protocol: Option<bool>,
273
274 #[clap(long, value_parser)]
277 #[serde(default)]
278 pub support_kitty_graphics_protocol: Option<bool>,
279
280 #[clap(long, value_parser)]
293 #[serde(default)]
294 pub web_server: Option<bool>,
295
296 #[clap(long, value_parser)]
309 #[serde(default)]
310 pub web_sharing: Option<WebSharing>,
311
312 #[clap(long, value_parser)]
315 #[serde(default)]
316 pub stacked_resize: Option<bool>,
317
318 #[clap(long, value_parser)]
319 #[serde(default)]
320 pub stacked_pane_list: Option<bool>,
321
322 #[clap(long, value_parser)]
325 #[serde(default)]
326 pub show_startup_tips: Option<bool>,
327
328 #[clap(long, value_parser)]
331 #[serde(default)]
332 pub show_release_notes: Option<bool>,
333
334 #[clap(long, value_parser)]
337 #[serde(default)]
338 pub advanced_mouse_actions: Option<bool>,
339
340 #[clap(long, value_parser)]
343 #[serde(default)]
344 pub mouse_scroll_resize: Option<bool>,
345
346 #[clap(long, value_parser)]
349 #[serde(default)]
350 pub scroll_mode_sync: Option<bool>,
351
352 #[clap(long, value_parser)]
355 #[serde(default)]
356 pub mouse_hover_effects: Option<bool>,
357
358 #[clap(long, value_parser)]
361 #[serde(default)]
362 pub mouse_hover_tips: Option<bool>,
363
364 #[clap(long, value_parser)]
367 #[serde(default)]
368 pub visual_bell: Option<bool>,
369
370 #[clap(long, value_parser)]
373 #[serde(default)]
374 pub focus_follows_mouse: Option<bool>,
375
376 #[clap(long, value_parser)]
379 #[serde(default)]
380 pub mouse_click_through: Option<bool>,
381
382 #[clap(long, value_parser)]
386 #[serde(default)]
387 pub osc133_command_selection: Option<bool>,
388
389 #[clap(long, value_parser)]
393 #[serde(default)]
394 pub word_separators: Option<String>,
395
396 #[clap(long, value_parser)]
397 #[serde(default)]
398 pub host_notification_protocol: Option<HostNotificationProtocol>,
399
400 pub web_server_ip: Option<IpAddr>,
403 pub web_server_port: Option<u16>,
404 pub web_server_cert: Option<PathBuf>,
405 pub web_server_key: Option<PathBuf>,
406 pub enforce_https_for_localhost: Option<bool>,
407 #[clap(long, value_parser)]
410 pub post_command_discovery_hook: Option<String>,
411
412 #[clap(long)]
418 pub client_async_worker_tasks: Option<usize>,
419
420 #[clap(long, value_enum, hide_possible_values = true, value_parser)]
423 #[serde(default)]
424 pub nested_session_handling: Option<NestedSessionHandling>,
425
426 #[clap(long, value_parser)]
427 #[serde(default)]
428 pub dangerously_enable_paste_buffer_read: Option<bool>,
429}
430
431#[derive(ValueEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
432pub enum Clipboard {
433 #[serde(alias = "system")]
434 System,
435 #[serde(alias = "primary")]
436 Primary,
437}
438
439impl Default for Clipboard {
440 fn default() -> Self {
441 Self::System
442 }
443}
444
445#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ValueEnum)]
446pub enum HostNotificationProtocol {
447 #[serde(alias = "auto")]
448 Auto,
449 #[serde(alias = "osc9")]
450 Osc9,
451 #[serde(alias = "osc99")]
452 Osc99,
453 #[serde(alias = "bell")]
454 Bell,
455 #[serde(alias = "off")]
456 Off,
457}
458
459impl Default for HostNotificationProtocol {
460 fn default() -> Self {
461 Self::Auto
462 }
463}
464
465impl HostNotificationProtocol {
466 pub fn as_str(&self) -> &'static str {
467 match self {
468 Self::Auto => "auto",
469 Self::Osc9 => "osc9",
470 Self::Osc99 => "osc99",
471 Self::Bell => "bell",
472 Self::Off => "off",
473 }
474 }
475}
476
477impl FromStr for HostNotificationProtocol {
478 type Err = String;
479 fn from_str(s: &str) -> Result<Self, Self::Err> {
480 match s {
481 "Auto" | "auto" => Ok(Self::Auto),
482 "Osc9" | "osc9" => Ok(Self::Osc9),
483 "Osc99" | "osc99" => Ok(Self::Osc99),
484 "Bell" | "bell" => Ok(Self::Bell),
485 "Off" | "off" => Ok(Self::Off),
486 _ => Err(format!("No such host_notification_protocol: {}", s)),
487 }
488 }
489}
490
491impl FromStr for Clipboard {
492 type Err = String;
493 fn from_str(s: &str) -> Result<Self, Self::Err> {
494 match s {
495 "System" | "system" => Ok(Self::System),
496 "Primary" | "primary" => Ok(Self::Primary),
497 _ => Err(format!("No such clipboard: {}", s)),
498 }
499 }
500}
501
502impl Options {
503 pub fn from_yaml(from_yaml: Option<Options>) -> Options {
504 if let Some(opts) = from_yaml {
505 opts
506 } else {
507 Options::default()
508 }
509 }
510 pub fn merge(&self, other: Options) -> Options {
514 let mouse_mode = other.mouse_mode.or(self.mouse_mode);
515 let pane_frames = other.pane_frames.or(self.pane_frames);
516 let pane_frame_style = other.pane_frame_style.or(self.pane_frame_style);
517 let auto_layout = other.auto_layout.or(self.auto_layout);
518 let mirror_session = other.mirror_session.or(self.mirror_session);
519 let simplified_ui = other.simplified_ui.or(self.simplified_ui);
520 let default_mode = other.default_mode.or(self.default_mode);
521 let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
522 let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
523 let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
524 let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
525 let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
526 let theme = other.theme.or_else(|| self.theme.clone());
527 let theme_dark = other.theme_dark.or_else(|| self.theme_dark.clone());
528 let theme_light = other.theme_light.or_else(|| self.theme_light.clone());
529 let explicit_theme_hue = other.explicit_theme_hue.or(self.explicit_theme_hue);
530 let on_force_close = other.on_force_close.or(self.on_force_close);
531 let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
532 let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
533 let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
534 let copy_on_select = other.copy_on_select.or(self.copy_on_select);
535 let osc8_hyperlinks = other.osc8_hyperlinks.or(self.osc8_hyperlinks);
536 let scrollback_editor = other
537 .scrollback_editor
538 .or_else(|| self.scrollback_editor.clone());
539 let session_name = other.session_name.or_else(|| self.session_name.clone());
540 let attach_to_session = other
541 .attach_to_session
542 .or_else(|| self.attach_to_session.clone());
543 let session_serialization = other.session_serialization.or(self.session_serialization);
544 let serialize_pane_viewport = other
545 .serialize_pane_viewport
546 .or(self.serialize_pane_viewport);
547 let scrollback_lines_to_serialize = other
548 .scrollback_lines_to_serialize
549 .or(self.scrollback_lines_to_serialize);
550 let styled_underlines = other.styled_underlines.or(self.styled_underlines);
551 let serialization_interval = other.serialization_interval.or(self.serialization_interval);
552 let disable_session_metadata = other
553 .disable_session_metadata
554 .or(self.disable_session_metadata);
555 let support_kitty_keyboard_protocol = other
556 .support_kitty_keyboard_protocol
557 .or(self.support_kitty_keyboard_protocol);
558 let support_kitty_graphics_protocol = other
559 .support_kitty_graphics_protocol
560 .or(self.support_kitty_graphics_protocol);
561 let web_server = other.web_server.or(self.web_server);
562 let web_sharing = other.web_sharing.or(self.web_sharing);
563 let stacked_resize = other.stacked_resize.or(self.stacked_resize);
564 let stacked_pane_list = other.stacked_pane_list.or(self.stacked_pane_list);
565 let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
566 let show_release_notes = other.show_release_notes.or(self.show_release_notes);
567 let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
568 let mouse_scroll_resize = other.mouse_scroll_resize.or(self.mouse_scroll_resize);
569 let scroll_mode_sync = other.scroll_mode_sync.or(self.scroll_mode_sync);
570 let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects);
571 let mouse_hover_tips = other.mouse_hover_tips.or(self.mouse_hover_tips);
572 let visual_bell = other.visual_bell.or(self.visual_bell);
573 let focus_follows_mouse = other.focus_follows_mouse.or(self.focus_follows_mouse);
574 let mouse_click_through = other.mouse_click_through.or(self.mouse_click_through);
575 let osc133_command_selection = other
576 .osc133_command_selection
577 .or(self.osc133_command_selection);
578 let word_separators = other
579 .word_separators
580 .or_else(|| self.word_separators.clone());
581 let host_notification_protocol = other
582 .host_notification_protocol
583 .or(self.host_notification_protocol);
584 let web_server_ip = other.web_server_ip.or(self.web_server_ip);
585 let web_server_port = other.web_server_port.or(self.web_server_port);
586 let web_server_cert = other
587 .web_server_cert
588 .or_else(|| self.web_server_cert.clone());
589 let web_server_key = other.web_server_key.or_else(|| self.web_server_key.clone());
590 let enforce_https_for_localhost = other
591 .enforce_https_for_localhost
592 .or(self.enforce_https_for_localhost);
593 let post_command_discovery_hook = other
594 .post_command_discovery_hook
595 .or(self.post_command_discovery_hook.clone());
596 let client_async_worker_tasks = other
597 .client_async_worker_tasks
598 .or(self.client_async_worker_tasks);
599 let nested_session_handling = other
600 .nested_session_handling
601 .or(self.nested_session_handling);
602 let dangerously_enable_paste_buffer_read = other
603 .dangerously_enable_paste_buffer_read
604 .or(self.dangerously_enable_paste_buffer_read);
605
606 Options {
607 simplified_ui,
608 theme,
609 theme_dark,
610 theme_light,
611 explicit_theme_hue,
612 default_mode,
613 default_shell,
614 default_cwd,
615 default_layout,
616 layout_dir,
617 theme_dir,
618 mouse_mode,
619 pane_frames,
620 pane_frame_style,
621 mirror_session,
622 on_force_close,
623 scroll_buffer_size,
624 copy_command,
625 copy_clipboard,
626 copy_on_select,
627 osc8_hyperlinks,
628 scrollback_editor,
629 session_name,
630 attach_to_session,
631 auto_layout,
632 session_serialization,
633 serialize_pane_viewport,
634 scrollback_lines_to_serialize,
635 styled_underlines,
636 serialization_interval,
637 disable_session_metadata,
638 support_kitty_keyboard_protocol,
639 support_kitty_graphics_protocol,
640 web_server,
641 web_sharing,
642 stacked_resize,
643 stacked_pane_list,
644 show_startup_tips,
645 show_release_notes,
646 advanced_mouse_actions,
647 mouse_scroll_resize,
648 scroll_mode_sync,
649 mouse_hover_effects,
650 mouse_hover_tips,
651 visual_bell,
652 focus_follows_mouse,
653 mouse_click_through,
654 osc133_command_selection,
655 word_separators,
656 host_notification_protocol,
657 web_server_ip,
658 web_server_port,
659 web_server_cert,
660 web_server_key,
661 enforce_https_for_localhost,
662 post_command_discovery_hook,
663 client_async_worker_tasks,
664 nested_session_handling,
665 dangerously_enable_paste_buffer_read,
666 }
667 }
668
669 pub fn merge_from_cli(&self, other: Options) -> Options {
674 let merge_bool = |opt_other: Option<bool>, opt_self: Option<bool>| {
675 if opt_other.is_some() ^ opt_self.is_some() {
676 opt_other.or(opt_self)
677 } else if opt_other.is_some() && opt_self.is_some() {
678 Some(opt_other.unwrap() ^ opt_self.unwrap())
679 } else {
680 None
681 }
682 };
683
684 let simplified_ui = merge_bool(other.simplified_ui, self.simplified_ui);
685 let mouse_mode = merge_bool(other.mouse_mode, self.mouse_mode);
686 let pane_frames = merge_bool(other.pane_frames, self.pane_frames);
687 let pane_frame_style = other.pane_frame_style.or(self.pane_frame_style);
688 let auto_layout = merge_bool(other.auto_layout, self.auto_layout);
689 let mirror_session = merge_bool(other.mirror_session, self.mirror_session);
690 let session_serialization =
691 merge_bool(other.session_serialization, self.session_serialization);
692 let serialize_pane_viewport =
693 merge_bool(other.serialize_pane_viewport, self.serialize_pane_viewport);
694
695 let default_mode = other.default_mode.or(self.default_mode);
696 let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
697 let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
698 let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
699 let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
700 let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
701 let theme = other.theme.or_else(|| self.theme.clone());
702 let theme_dark = other.theme_dark.or_else(|| self.theme_dark.clone());
703 let theme_light = other.theme_light.or_else(|| self.theme_light.clone());
704 let explicit_theme_hue = other.explicit_theme_hue.or(self.explicit_theme_hue);
705 let on_force_close = other.on_force_close.or(self.on_force_close);
706 let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
707 let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
708 let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
709 let copy_on_select = other.copy_on_select.or(self.copy_on_select);
710 let osc8_hyperlinks = other.osc8_hyperlinks.or(self.osc8_hyperlinks);
711 let scrollback_editor = other
712 .scrollback_editor
713 .or_else(|| self.scrollback_editor.clone());
714 let session_name = other.session_name.or_else(|| self.session_name.clone());
715 let attach_to_session = other
716 .attach_to_session
717 .or_else(|| self.attach_to_session.clone());
718 let scrollback_lines_to_serialize = other
719 .scrollback_lines_to_serialize
720 .or_else(|| self.scrollback_lines_to_serialize.clone());
721 let styled_underlines = other.styled_underlines.or(self.styled_underlines);
722 let serialization_interval = other.serialization_interval.or(self.serialization_interval);
723 let disable_session_metadata = other
724 .disable_session_metadata
725 .or(self.disable_session_metadata);
726 let support_kitty_keyboard_protocol = other
727 .support_kitty_keyboard_protocol
728 .or(self.support_kitty_keyboard_protocol);
729 let support_kitty_graphics_protocol = other
730 .support_kitty_graphics_protocol
731 .or(self.support_kitty_graphics_protocol);
732 let web_server = other.web_server.or(self.web_server);
733 let web_sharing = other.web_sharing.or(self.web_sharing);
734 let stacked_resize = other.stacked_resize.or(self.stacked_resize);
735 let stacked_pane_list = other.stacked_pane_list.or(self.stacked_pane_list);
736 let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
737 let show_release_notes = other.show_release_notes.or(self.show_release_notes);
738 let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
739 let mouse_scroll_resize = other.mouse_scroll_resize.or(self.mouse_scroll_resize);
740 let scroll_mode_sync = other.scroll_mode_sync.or(self.scroll_mode_sync);
741 let mouse_hover_effects = other.mouse_hover_effects.or(self.mouse_hover_effects);
742 let mouse_hover_tips = other.mouse_hover_tips.or(self.mouse_hover_tips);
743 let visual_bell = other.visual_bell.or(self.visual_bell);
744 let focus_follows_mouse = merge_bool(other.focus_follows_mouse, self.focus_follows_mouse);
745 let mouse_click_through = merge_bool(other.mouse_click_through, self.mouse_click_through);
746 let osc133_command_selection = other
747 .osc133_command_selection
748 .or(self.osc133_command_selection);
749 let word_separators = other
750 .word_separators
751 .or_else(|| self.word_separators.clone());
752 let host_notification_protocol = other
753 .host_notification_protocol
754 .or(self.host_notification_protocol);
755 let web_server_ip = other.web_server_ip.or(self.web_server_ip);
756 let web_server_port = other.web_server_port.or(self.web_server_port);
757 let web_server_cert = other
758 .web_server_cert
759 .or_else(|| self.web_server_cert.clone());
760 let web_server_key = other.web_server_key.or_else(|| self.web_server_key.clone());
761 let enforce_https_for_localhost = other
762 .enforce_https_for_localhost
763 .or(self.enforce_https_for_localhost);
764 let post_command_discovery_hook = other
765 .post_command_discovery_hook
766 .or_else(|| self.post_command_discovery_hook.clone());
767 let client_async_worker_tasks = other
768 .client_async_worker_tasks
769 .or(self.client_async_worker_tasks);
770 let nested_session_handling = other
771 .nested_session_handling
772 .or(self.nested_session_handling);
773 let dangerously_enable_paste_buffer_read = other
774 .dangerously_enable_paste_buffer_read
775 .or(self.dangerously_enable_paste_buffer_read);
776
777 Options {
778 simplified_ui,
779 theme,
780 theme_dark,
781 theme_light,
782 explicit_theme_hue,
783 default_mode,
784 default_shell,
785 default_cwd,
786 default_layout,
787 layout_dir,
788 theme_dir,
789 mouse_mode,
790 pane_frames,
791 pane_frame_style,
792 mirror_session,
793 on_force_close,
794 scroll_buffer_size,
795 copy_command,
796 copy_clipboard,
797 copy_on_select,
798 osc8_hyperlinks,
799 scrollback_editor,
800 session_name,
801 attach_to_session,
802 auto_layout,
803 session_serialization,
804 serialize_pane_viewport,
805 scrollback_lines_to_serialize,
806 styled_underlines,
807 serialization_interval,
808 disable_session_metadata,
809 support_kitty_keyboard_protocol,
810 support_kitty_graphics_protocol,
811 web_server,
812 web_sharing,
813 stacked_resize,
814 stacked_pane_list,
815 show_startup_tips,
816 show_release_notes,
817 advanced_mouse_actions,
818 mouse_scroll_resize,
819 scroll_mode_sync,
820 mouse_hover_effects,
821 mouse_hover_tips,
822 visual_bell,
823 focus_follows_mouse,
824 mouse_click_through,
825 osc133_command_selection,
826 word_separators,
827 host_notification_protocol,
828 web_server_ip,
829 web_server_port,
830 web_server_cert,
831 web_server_key,
832 enforce_https_for_localhost,
833 post_command_discovery_hook,
834 client_async_worker_tasks,
835 nested_session_handling,
836 dangerously_enable_paste_buffer_read,
837 }
838 }
839
840 pub fn from_cli(&self, other: Option<Command>) -> Options {
841 if let Some(Command::Options(options)) = other {
842 Options::merge_from_cli(self, options.into())
843 } else {
844 self.to_owned()
845 }
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852
853 #[test]
854 fn pane_frame_style_from_str_accepts_all_variants() {
855 assert_eq!(
856 "full".parse::<PaneFrameStyle>().unwrap(),
857 PaneFrameStyle::Full
858 );
859 assert_eq!(
860 "titles".parse::<PaneFrameStyle>().unwrap(),
861 PaneFrameStyle::Titles
862 );
863 assert_eq!(
864 "none".parse::<PaneFrameStyle>().unwrap(),
865 PaneFrameStyle::None
866 );
867 assert_eq!(
868 "NONE".parse::<PaneFrameStyle>().unwrap(),
869 PaneFrameStyle::None
870 );
871 assert!("bogus".parse::<PaneFrameStyle>().is_err());
872 }
873
874 #[test]
875 fn host_notification_protocol_from_str_accepts_all_variants() {
876 assert_eq!(
877 "auto".parse::<HostNotificationProtocol>().unwrap(),
878 HostNotificationProtocol::Auto
879 );
880 assert_eq!(
881 "osc9".parse::<HostNotificationProtocol>().unwrap(),
882 HostNotificationProtocol::Osc9
883 );
884 assert_eq!(
885 "osc99".parse::<HostNotificationProtocol>().unwrap(),
886 HostNotificationProtocol::Osc99
887 );
888 assert_eq!(
889 "bell".parse::<HostNotificationProtocol>().unwrap(),
890 HostNotificationProtocol::Bell
891 );
892 assert_eq!(
893 "off".parse::<HostNotificationProtocol>().unwrap(),
894 HostNotificationProtocol::Off
895 );
896 assert!("bogus".parse::<HostNotificationProtocol>().is_err());
897 }
898
899 #[test]
900 fn every_host_notification_protocol_variant_stringifies_back_to_itself() {
901 for variant in [
902 HostNotificationProtocol::Auto,
903 HostNotificationProtocol::Osc9,
904 HostNotificationProtocol::Osc99,
905 HostNotificationProtocol::Bell,
906 HostNotificationProtocol::Off,
907 ] {
908 assert_eq!(
909 variant
910 .as_str()
911 .parse::<HostNotificationProtocol>()
912 .unwrap(),
913 variant
914 );
915 }
916 }
917
918 #[test]
919 fn the_host_notification_protocol_defaults_to_auto() {
920 assert_eq!(
921 HostNotificationProtocol::default(),
922 HostNotificationProtocol::Auto
923 );
924 }
925
926 #[test]
927 fn a_configured_host_notification_protocol_is_overridden_by_the_merged_in_one() {
928 let config = Options {
929 host_notification_protocol: Some(HostNotificationProtocol::Osc9),
930 ..Default::default()
931 };
932 let layout = Options {
933 host_notification_protocol: Some(HostNotificationProtocol::Bell),
934 ..Default::default()
935 };
936 assert_eq!(
937 config.merge(layout).host_notification_protocol,
938 Some(HostNotificationProtocol::Bell)
939 );
940 }
941
942 #[test]
943 fn an_unset_host_notification_protocol_does_not_clobber_the_configured_one() {
944 let config = Options {
945 host_notification_protocol: Some(HostNotificationProtocol::Osc9),
946 ..Default::default()
947 };
948 assert_eq!(
949 config.merge(Options::default()).host_notification_protocol,
950 Some(HostNotificationProtocol::Osc9)
951 );
952 }
953
954 #[test]
955 fn a_host_notification_protocol_unset_everywhere_stays_unset() {
956 assert_eq!(
957 Options::default()
958 .merge(Options::default())
959 .host_notification_protocol,
960 None
961 );
962 assert_eq!(
963 Options::default()
964 .merge_from_cli(Options::default())
965 .host_notification_protocol,
966 None
967 );
968 }
969
970 #[test]
971 fn a_host_notification_protocol_given_on_the_command_line_wins() {
972 let config = Options {
973 host_notification_protocol: Some(HostNotificationProtocol::Osc9),
974 ..Default::default()
975 };
976 let cli = Options {
977 host_notification_protocol: Some(HostNotificationProtocol::Off),
978 ..Default::default()
979 };
980 assert_eq!(
981 config.merge_from_cli(cli).host_notification_protocol,
982 Some(HostNotificationProtocol::Off)
983 );
984 }
985
986 #[test]
987 fn a_host_notification_protocol_absent_from_the_command_line_keeps_the_configured_one() {
988 let config = Options {
989 host_notification_protocol: Some(HostNotificationProtocol::Osc99),
990 ..Default::default()
991 };
992 assert_eq!(
993 config
994 .merge_from_cli(Options::default())
995 .host_notification_protocol,
996 Some(HostNotificationProtocol::Osc99),
997 "the option is carried over verbatim, not toggled like the boolean options are"
998 );
999 }
1000}