* initial implementation with break panes to new tab * break pane group left/right * group embed/eject panes * stack pane group on resize * close pane group * style(fmt): rustfmt * fix tests * group drag and ungroup with the mouse * fix mouse hover for multiple clients * fix for multiple clients * multiple select plugin initial * use real data in plugin * adjust functionality * fix some ux issues * reflect group mouse group selections in plugin * group/ungroup panes in Zellij * highlight frames when marked by the plugin * refactor: render function in plugin * some ui responsiveness * some more responsiveness and adjust hover text * break out functionality * stack functionality * break panes left/right and close multiple panes * fix(tab): only relayout the relevant layout when non-focused pane is closed * status bar UI * embed and float panes * work * fix some ui/ux issues * refactor: move stuff around * some responsiveness and fix search result browsing bug * change plugin pane title * differentiate group from focused pane * add keyboard shortcut * add ui to compact bar * make boundary colors appear properly without pane frames * get plugins to also display their frame color * make hover shortcuts appear on command panes * fix: do not render search string component if it's empty * BeforeClose Event and unhighlight panes on exit * some UI/UX fixes * some fixes to the catppuccin-latte theme * remove ungroup shortcut * make some ui components opaque * fix more opaque elements * fix some issues with stacking pane order * keyboard shortcuts for grouping * config to opt out of advanced mouse actions * make selected + focused frame color distinct * group marking mode * refactor: multiple-select plugin * adjust stacking group behavior * adjust flashing periods * render common modifier in group controls * add to compact bar * adjust key hint wording * add key to presets and default config * some cleanups * some refactoring * fix tests * fix plugin system tests * tests: group/ungroup/hover * test: BeforeClose plugin event * new plugin assets * style(fmt): rustfmt * remove warnings * tests: give plugin more time to load
470 lines
18 KiB
Rust
470 lines
18 KiB
Rust
//! Handles cli and configuration options
|
|
use crate::cli::Command;
|
|
use crate::data::InputMode;
|
|
use clap::{ArgEnum, Args};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::path::PathBuf;
|
|
use std::str::FromStr;
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ArgEnum)]
|
|
pub enum OnForceClose {
|
|
#[serde(alias = "quit")]
|
|
Quit,
|
|
#[serde(alias = "detach")]
|
|
Detach,
|
|
}
|
|
|
|
impl Default for OnForceClose {
|
|
fn default() -> Self {
|
|
Self::Detach
|
|
}
|
|
}
|
|
|
|
impl FromStr for OnForceClose {
|
|
type Err = Box<dyn std::error::Error>;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"quit" => Ok(Self::Quit),
|
|
"detach" => Ok(Self::Detach),
|
|
e => Err(e.to_string().into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize, Args)]
|
|
/// Options that can be set either through the config file,
|
|
/// or cli flags - cli flags should take precedence over the config file
|
|
/// TODO: In order to correctly parse boolean flags, this is currently split
|
|
/// into Options and CliOptions, this could be a good canditate for a macro
|
|
pub struct Options {
|
|
/// Allow plugins to use a more simplified layout
|
|
/// that is compatible with more fonts (true or false)
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub simplified_ui: Option<bool>,
|
|
/// Set the default theme
|
|
#[clap(long, value_parser)]
|
|
pub theme: Option<String>,
|
|
/// Set the default mode
|
|
#[clap(long, arg_enum, hide_possible_values = true, value_parser)]
|
|
pub default_mode: Option<InputMode>,
|
|
/// Set the default shell
|
|
#[clap(long, value_parser)]
|
|
pub default_shell: Option<PathBuf>,
|
|
/// Set the default cwd
|
|
#[clap(long, value_parser)]
|
|
pub default_cwd: Option<PathBuf>,
|
|
/// Set the default layout
|
|
#[clap(long, value_parser)]
|
|
pub default_layout: Option<PathBuf>,
|
|
/// Set the layout_dir, defaults to
|
|
/// subdirectory of config dir
|
|
#[clap(long, value_parser)]
|
|
pub layout_dir: Option<PathBuf>,
|
|
/// Set the theme_dir, defaults to
|
|
/// subdirectory of config dir
|
|
#[clap(long, value_parser)]
|
|
pub theme_dir: Option<PathBuf>,
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
/// Set the handling of mouse events (true or false)
|
|
/// Can be temporarily bypassed by the [SHIFT] key
|
|
pub mouse_mode: Option<bool>,
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
/// Set display of the pane frames (true or false)
|
|
pub pane_frames: Option<bool>,
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
/// Mirror session when multiple users are connected (true or false)
|
|
pub mirror_session: Option<bool>,
|
|
/// Set behaviour on force close (quit or detach)
|
|
#[clap(long, arg_enum, hide_possible_values = true, value_parser)]
|
|
pub on_force_close: Option<OnForceClose>,
|
|
#[clap(long, value_parser)]
|
|
pub scroll_buffer_size: Option<usize>,
|
|
|
|
/// Switch to using a user supplied command for clipboard instead of OSC52
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub copy_command: Option<String>,
|
|
|
|
/// OSC52 destination clipboard
|
|
#[clap(
|
|
long,
|
|
arg_enum,
|
|
ignore_case = true,
|
|
conflicts_with = "copy-command",
|
|
value_parser
|
|
)]
|
|
#[serde(default)]
|
|
pub copy_clipboard: Option<Clipboard>,
|
|
|
|
/// Automatically copy when selecting text (true or false)
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub copy_on_select: Option<bool>,
|
|
|
|
/// Explicit full path to open the scrollback editor (default is $EDITOR or $VISUAL)
|
|
#[clap(long, value_parser)]
|
|
pub scrollback_editor: Option<PathBuf>,
|
|
|
|
/// The name of the session to create when starting Zellij
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub session_name: Option<String>,
|
|
|
|
/// Whether to attach to a session specified in "session-name" if it exists
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub attach_to_session: Option<bool>,
|
|
|
|
/// Whether to lay out panes in a predefined set of layouts whenever possible
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub auto_layout: Option<bool>,
|
|
|
|
/// Whether sessions should be serialized to the HD so that they can be later resurrected,
|
|
/// default is true
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub session_serialization: Option<bool>,
|
|
|
|
/// Whether pane viewports are serialized along with the session, default is false
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub serialize_pane_viewport: Option<bool>,
|
|
|
|
/// Scrollback lines to serialize along with the pane viewport when serializing sessions, 0
|
|
/// defaults to the scrollback size. If this number is higher than the scrollback size, it will
|
|
/// also default to the scrollback size
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub scrollback_lines_to_serialize: Option<usize>,
|
|
|
|
/// Whether to use ANSI styled underlines
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub styled_underlines: Option<bool>,
|
|
|
|
/// The interval at which to serialize sessions for resurrection (in seconds)
|
|
#[clap(long, value_parser)]
|
|
pub serialization_interval: Option<u64>,
|
|
|
|
/// If true, will disable writing session metadata to disk
|
|
#[clap(long, value_parser)]
|
|
pub disable_session_metadata: Option<bool>,
|
|
|
|
/// Whether to enable support for the Kitty keyboard protocol (must also be supported by the
|
|
/// host terminal), defaults to true if the terminal supports it
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub support_kitty_keyboard_protocol: Option<bool>,
|
|
|
|
/// Whether to stack panes when resizing beyond a certain size
|
|
/// default is true
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub stacked_resize: Option<bool>,
|
|
|
|
/// Whether to show startup tips when starting a new session
|
|
/// default is true
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub show_startup_tips: Option<bool>,
|
|
|
|
/// Whether to show release notes on first run of a new version
|
|
/// default is true
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub show_release_notes: Option<bool>,
|
|
|
|
/// Whether to enable mouse hover effects and pane grouping functionality
|
|
/// default is true
|
|
#[clap(long, value_parser)]
|
|
#[serde(default)]
|
|
pub advanced_mouse_actions: Option<bool>,
|
|
}
|
|
|
|
#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
|
|
pub enum Clipboard {
|
|
#[serde(alias = "system")]
|
|
System,
|
|
#[serde(alias = "primary")]
|
|
Primary,
|
|
}
|
|
|
|
impl Default for Clipboard {
|
|
fn default() -> Self {
|
|
Self::System
|
|
}
|
|
}
|
|
|
|
impl FromStr for Clipboard {
|
|
type Err = String;
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"System" | "system" => Ok(Self::System),
|
|
"Primary" | "primary" => Ok(Self::Primary),
|
|
_ => Err(format!("No such clipboard: {}", s)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Options {
|
|
pub fn from_yaml(from_yaml: Option<Options>) -> Options {
|
|
if let Some(opts) = from_yaml {
|
|
opts
|
|
} else {
|
|
Options::default()
|
|
}
|
|
}
|
|
/// Merges two [`Options`] structs, a `Some` in `other`
|
|
/// will supersede a `Some` in `self`
|
|
// TODO: Maybe a good candidate for a macro?
|
|
pub fn merge(&self, other: Options) -> Options {
|
|
let mouse_mode = other.mouse_mode.or(self.mouse_mode);
|
|
let pane_frames = other.pane_frames.or(self.pane_frames);
|
|
let auto_layout = other.auto_layout.or(self.auto_layout);
|
|
let mirror_session = other.mirror_session.or(self.mirror_session);
|
|
let simplified_ui = other.simplified_ui.or(self.simplified_ui);
|
|
let default_mode = other.default_mode.or(self.default_mode);
|
|
let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
|
|
let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
|
|
let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
|
|
let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
|
|
let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
|
|
let theme = other.theme.or_else(|| self.theme.clone());
|
|
let on_force_close = other.on_force_close.or(self.on_force_close);
|
|
let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
|
|
let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
|
|
let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
|
|
let copy_on_select = other.copy_on_select.or(self.copy_on_select);
|
|
let scrollback_editor = other
|
|
.scrollback_editor
|
|
.or_else(|| self.scrollback_editor.clone());
|
|
let session_name = other.session_name.or_else(|| self.session_name.clone());
|
|
let attach_to_session = other
|
|
.attach_to_session
|
|
.or_else(|| self.attach_to_session.clone());
|
|
let session_serialization = other.session_serialization.or(self.session_serialization);
|
|
let serialize_pane_viewport = other
|
|
.serialize_pane_viewport
|
|
.or(self.serialize_pane_viewport);
|
|
let scrollback_lines_to_serialize = other
|
|
.scrollback_lines_to_serialize
|
|
.or(self.scrollback_lines_to_serialize);
|
|
let styled_underlines = other.styled_underlines.or(self.styled_underlines);
|
|
let serialization_interval = other.serialization_interval.or(self.serialization_interval);
|
|
let disable_session_metadata = other
|
|
.disable_session_metadata
|
|
.or(self.disable_session_metadata);
|
|
let support_kitty_keyboard_protocol = other
|
|
.support_kitty_keyboard_protocol
|
|
.or(self.support_kitty_keyboard_protocol);
|
|
let stacked_resize = other.stacked_resize.or(self.stacked_resize);
|
|
let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
|
|
let show_release_notes = other.show_release_notes.or(self.show_release_notes);
|
|
let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
|
|
|
|
Options {
|
|
simplified_ui,
|
|
theme,
|
|
default_mode,
|
|
default_shell,
|
|
default_cwd,
|
|
default_layout,
|
|
layout_dir,
|
|
theme_dir,
|
|
mouse_mode,
|
|
pane_frames,
|
|
mirror_session,
|
|
on_force_close,
|
|
scroll_buffer_size,
|
|
copy_command,
|
|
copy_clipboard,
|
|
copy_on_select,
|
|
scrollback_editor,
|
|
session_name,
|
|
attach_to_session,
|
|
auto_layout,
|
|
session_serialization,
|
|
serialize_pane_viewport,
|
|
scrollback_lines_to_serialize,
|
|
styled_underlines,
|
|
serialization_interval,
|
|
disable_session_metadata,
|
|
support_kitty_keyboard_protocol,
|
|
stacked_resize,
|
|
show_startup_tips,
|
|
show_release_notes,
|
|
advanced_mouse_actions,
|
|
}
|
|
}
|
|
|
|
/// Merges two [`Options`] structs,
|
|
/// - `Some` in `other` will supersede a `Some` in `self`
|
|
/// - `Some(bool)` in `other` will toggle a `Some(bool)` in `self`
|
|
// TODO: Maybe a good candidate for a macro?
|
|
pub fn merge_from_cli(&self, other: Options) -> Options {
|
|
let merge_bool = |opt_other: Option<bool>, opt_self: Option<bool>| {
|
|
if opt_other.is_some() ^ opt_self.is_some() {
|
|
opt_other.or(opt_self)
|
|
} else if opt_other.is_some() && opt_self.is_some() {
|
|
Some(opt_other.unwrap() ^ opt_self.unwrap())
|
|
} else {
|
|
None
|
|
}
|
|
};
|
|
|
|
let simplified_ui = merge_bool(other.simplified_ui, self.simplified_ui);
|
|
let mouse_mode = merge_bool(other.mouse_mode, self.mouse_mode);
|
|
let pane_frames = merge_bool(other.pane_frames, self.pane_frames);
|
|
let auto_layout = merge_bool(other.auto_layout, self.auto_layout);
|
|
let mirror_session = merge_bool(other.mirror_session, self.mirror_session);
|
|
let session_serialization =
|
|
merge_bool(other.session_serialization, self.session_serialization);
|
|
let serialize_pane_viewport =
|
|
merge_bool(other.serialize_pane_viewport, self.serialize_pane_viewport);
|
|
|
|
let default_mode = other.default_mode.or(self.default_mode);
|
|
let default_shell = other.default_shell.or_else(|| self.default_shell.clone());
|
|
let default_cwd = other.default_cwd.or_else(|| self.default_cwd.clone());
|
|
let default_layout = other.default_layout.or_else(|| self.default_layout.clone());
|
|
let layout_dir = other.layout_dir.or_else(|| self.layout_dir.clone());
|
|
let theme_dir = other.theme_dir.or_else(|| self.theme_dir.clone());
|
|
let theme = other.theme.or_else(|| self.theme.clone());
|
|
let on_force_close = other.on_force_close.or(self.on_force_close);
|
|
let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
|
|
let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
|
|
let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
|
|
let copy_on_select = other.copy_on_select.or(self.copy_on_select);
|
|
let scrollback_editor = other
|
|
.scrollback_editor
|
|
.or_else(|| self.scrollback_editor.clone());
|
|
let session_name = other.session_name.or_else(|| self.session_name.clone());
|
|
let attach_to_session = other
|
|
.attach_to_session
|
|
.or_else(|| self.attach_to_session.clone());
|
|
let scrollback_lines_to_serialize = other
|
|
.scrollback_lines_to_serialize
|
|
.or_else(|| self.scrollback_lines_to_serialize.clone());
|
|
let styled_underlines = other.styled_underlines.or(self.styled_underlines);
|
|
let serialization_interval = other.serialization_interval.or(self.serialization_interval);
|
|
let disable_session_metadata = other
|
|
.disable_session_metadata
|
|
.or(self.disable_session_metadata);
|
|
let support_kitty_keyboard_protocol = other
|
|
.support_kitty_keyboard_protocol
|
|
.or(self.support_kitty_keyboard_protocol);
|
|
let stacked_resize = other.stacked_resize.or(self.stacked_resize);
|
|
let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips);
|
|
let show_release_notes = other.show_release_notes.or(self.show_release_notes);
|
|
let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions);
|
|
|
|
Options {
|
|
simplified_ui,
|
|
theme,
|
|
default_mode,
|
|
default_shell,
|
|
default_cwd,
|
|
default_layout,
|
|
layout_dir,
|
|
theme_dir,
|
|
mouse_mode,
|
|
pane_frames,
|
|
mirror_session,
|
|
on_force_close,
|
|
scroll_buffer_size,
|
|
copy_command,
|
|
copy_clipboard,
|
|
copy_on_select,
|
|
scrollback_editor,
|
|
session_name,
|
|
attach_to_session,
|
|
auto_layout,
|
|
session_serialization,
|
|
serialize_pane_viewport,
|
|
scrollback_lines_to_serialize,
|
|
styled_underlines,
|
|
serialization_interval,
|
|
disable_session_metadata,
|
|
support_kitty_keyboard_protocol,
|
|
stacked_resize,
|
|
show_startup_tips,
|
|
show_release_notes,
|
|
advanced_mouse_actions,
|
|
}
|
|
}
|
|
|
|
pub fn from_cli(&self, other: Option<Command>) -> Options {
|
|
if let Some(Command::Options(options)) = other {
|
|
Options::merge_from_cli(self, options.into())
|
|
} else {
|
|
self.to_owned()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default, Debug, PartialEq, Args, Serialize, Deserialize)]
|
|
/// Options that can be set through cli flags
|
|
/// boolean flags end up toggling boolean options in `Options`
|
|
pub struct CliOptions {
|
|
/// Disable handling of mouse events
|
|
#[clap(long, conflicts_with("mouse-mode"), value_parser)]
|
|
pub disable_mouse_mode: bool,
|
|
/// Disable display of pane frames
|
|
#[clap(long, conflicts_with("pane-frames"), value_parser)]
|
|
pub no_pane_frames: bool,
|
|
#[clap(flatten)]
|
|
pub options: Options,
|
|
}
|
|
|
|
impl From<CliOptions> for Options {
|
|
fn from(cli_options: CliOptions) -> Self {
|
|
let mut opts = cli_options.options;
|
|
|
|
// TODO: what?
|
|
if cli_options.no_pane_frames {
|
|
opts.pane_frames = Some(false);
|
|
}
|
|
if cli_options.disable_mouse_mode {
|
|
opts.mouse_mode = Some(false);
|
|
}
|
|
|
|
Self {
|
|
simplified_ui: opts.simplified_ui,
|
|
theme: opts.theme,
|
|
default_mode: opts.default_mode,
|
|
default_shell: opts.default_shell,
|
|
default_cwd: opts.default_cwd,
|
|
default_layout: opts.default_layout,
|
|
layout_dir: opts.layout_dir,
|
|
theme_dir: opts.theme_dir,
|
|
mouse_mode: opts.mouse_mode,
|
|
pane_frames: opts.pane_frames,
|
|
mirror_session: opts.mirror_session,
|
|
on_force_close: opts.on_force_close,
|
|
scroll_buffer_size: opts.scroll_buffer_size,
|
|
copy_command: opts.copy_command,
|
|
copy_clipboard: opts.copy_clipboard,
|
|
copy_on_select: opts.copy_on_select,
|
|
scrollback_editor: opts.scrollback_editor,
|
|
session_name: opts.session_name,
|
|
attach_to_session: opts.attach_to_session,
|
|
auto_layout: opts.auto_layout,
|
|
session_serialization: opts.session_serialization,
|
|
serialize_pane_viewport: opts.serialize_pane_viewport,
|
|
scrollback_lines_to_serialize: opts.scrollback_lines_to_serialize,
|
|
styled_underlines: opts.styled_underlines,
|
|
serialization_interval: opts.serialization_interval,
|
|
support_kitty_keyboard_protocol: opts.support_kitty_keyboard_protocol,
|
|
stacked_resize: opts.stacked_resize,
|
|
show_startup_tips: opts.show_startup_tips,
|
|
show_release_notes: opts.show_release_notes,
|
|
advanced_mouse_actions: opts.advanced_mouse_actions,
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|