zellij/zellij-client/src/old_config_converter/old_config.rs
noyez c8f3b94fde
fix(converter): YAML => KDL conversion with backslash hotkey. (#1879)
* Fixing YAML => KDL conversion with backslash hotkey.

Previously if the hotkey of backslash was used the yaml => kdl
conversion would create a KDL statement like so: `bind "\" {...}`.
That is incorrect kdl syntax since the backslash escapes the following
quote character. A way to get proper KDL is `bind r"\" {...}`. This
commit changes if the old HotKey is a backslash a properly creates KDL
syntax to address the backslash character.

* rustfmt
2022-11-02 16:39:56 +01:00

1298 lines
42 KiB
Rust

// This is a converter from the old yaml config to the new KDL config.
//
// It is supposed to be mostly self containing - please refrain from adding to it, importing
// from it or changing it
use std::fmt;
use std::path::PathBuf;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::{BTreeMap, HashMap};
use url::Url;
const ON_FORCE_CLOSE_DESCRIPTION: &'static str = "
// Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP
// eg. when terminal window with an active zellij session is closed
// Options:
// - detach (Default)
// - quit
//
";
const SIMPLIFIED_UI_DESCRIPTION: &'static str = "
// Send a request for a simplified ui (without arrow fonts) to plugins
// Options:
// - true
// - false (Default)
//
";
const DEFAULT_SHELL_DESCRIPTION: &'static str = "
// Choose the path to the default shell that zellij will use for opening new panes
// Default: $SHELL
//
";
const PANE_FRAMES_DESCRIPTION: &'static str = "
// Toggle between having pane frames around the panes
// Options:
// - true (default)
// - false
//
";
const DEFAULT_THEME_DESCRIPTION: &'static str = "
// Choose the theme that is specified in the themes section.
// Default: default
//
";
const DEFAULT_MODE_DESCRIPTION: &'static str = "
// Choose the mode that zellij uses when starting up.
// Default: normal
//
";
const MOUSE_MODE_DESCRIPTION: &'static str = "
// Toggle enabling the mouse mode.
// On certain configurations, or terminals this could
// potentially interfere with copying text.
// Options:
// - true (default)
// - false
//
";
const SCROLL_BUFFER_SIZE_DESCRIPTION: &'static str = "
// Configure the scroll back buffer size
// This is the number of lines zellij stores for each pane in the scroll back
// buffer. Excess number of lines are discarded in a FIFO fashion.
// Valid values: positive integers
// Default value: 10000
//
";
const COPY_COMMAND_DESCRIPTION: &'static str = "
// Provide a command to execute when copying text. The text will be piped to
// the stdin of the program to perform the copy. This can be used with
// terminal emulators which do not support the OSC 52 ANSI control sequence
// that will be used by default if this option is not set.
// Examples:
//
// copy_command \"xclip -selection clipboard\" // x11
// copy_command \"wl-copy\" // wayland
// copy_command \"pbcopy\" // osx
";
const COPY_CLIPBOARD_DESCRIPTION: &'static str = "
// Choose the destination for copied text
// Allows using the primary selection buffer (on x11/wayland) instead of the system clipboard.
// Does not apply when using copy_command.
// Options:
// - system (default)
// - primary
//
";
const COPY_ON_SELECT_DESCRIPTION: &'static str = "
// Enable or disable automatic copy (and clear) of selection when releasing mouse
// Default: true
//
";
const SCROLLBACK_EDITOR_DESCRIPTION: &'static str = "
// Path to the default editor to use to edit pane scrollbuffer
// Default: $EDITOR or $VISUAL
//
";
const MIRROR_SESSION_DESCRIPTION: &'static str = "
// When attaching to an existing session with other users,
// should the session be mirrored (true)
// or should each user have their own cursor (false)
// Default: false
//
";
const DEFAULT_LAYOUT_DESCRIPTION: &'static str = "
// The name of the default layout to load on startup
// Default: \"default\"
//
";
const LAYOUT_DIR_DESCRIPTION: &'static str = "
// The folder in which Zellij will look for layouts
//
";
const THEME_DIR_DESCRIPTION: &'static str = "
// The folder in which Zellij will look for themes
//
";
fn options_yaml_to_options_kdl(options_yaml: &OldOptions, no_comments: bool) -> String {
let mut options_kdl = String::new();
macro_rules! push_option {
($attribute_name:ident, $description_text:ident, $present_pattern:expr) => {
if !no_comments {
options_kdl.push_str($description_text);
}
if let Some($attribute_name) = &options_yaml.$attribute_name {
options_kdl.push_str(&format!($present_pattern, $attribute_name));
options_kdl.push('\n');
};
};
($attribute_name:ident, $description_text:ident, $present_pattern:expr, $absent_pattern:expr) => {
if !no_comments {
options_kdl.push_str($description_text);
}
match &options_yaml.$attribute_name {
Some($attribute_name) => {
options_kdl.push_str(&format!($present_pattern, $attribute_name));
},
None => {
if !no_comments {
options_kdl.push_str(&format!($absent_pattern));
}
},
};
if !no_comments || options_yaml.$attribute_name.is_some() {
options_kdl.push('\n');
}
};
}
push_option!(
on_force_close,
ON_FORCE_CLOSE_DESCRIPTION,
"on_force_close \"{}\"",
"// on_force_close \"quit\""
);
push_option!(
simplified_ui,
SIMPLIFIED_UI_DESCRIPTION,
"simplified_ui {}",
"// simplified_ui true"
);
push_option!(
default_shell,
DEFAULT_SHELL_DESCRIPTION,
"default_shell {:?}",
"// default_shell \"fish\""
);
push_option!(
pane_frames,
PANE_FRAMES_DESCRIPTION,
"pane_frames {}",
"// pane_frames true"
);
push_option!(
theme,
DEFAULT_THEME_DESCRIPTION,
"theme {:?} ",
"// theme \"default\""
);
push_option!(
default_layout,
DEFAULT_LAYOUT_DESCRIPTION,
"default_layout {:?}",
"// default_layout \"compact\""
);
push_option!(
default_mode,
DEFAULT_MODE_DESCRIPTION,
"default_mode \"{}\"",
"// default_mode \"locked\""
);
push_option!(
mouse_mode,
MOUSE_MODE_DESCRIPTION,
"mouse_mode {}",
"// mouse_mode false"
);
push_option!(
scroll_buffer_size,
SCROLL_BUFFER_SIZE_DESCRIPTION,
"scroll_buffer_size {}",
"// scroll_buffer_size 10000"
);
push_option!(copy_command, COPY_COMMAND_DESCRIPTION, "copy_command {:?}");
push_option!(
copy_clipboard,
COPY_CLIPBOARD_DESCRIPTION,
"copy_clipboard \"{}\"",
"// copy_clipboard \"primary\""
);
push_option!(
copy_on_select,
COPY_ON_SELECT_DESCRIPTION,
"copy_on_select {}",
"// copy_on_select false"
);
push_option!(
scrollback_editor,
SCROLLBACK_EDITOR_DESCRIPTION,
"scrollback_editor {:?}",
"// scrollback_editor \"/usr/bin/vim\""
);
push_option!(
mirror_session,
MIRROR_SESSION_DESCRIPTION,
"mirror_session {}",
"// mirror_session true"
);
push_option!(
layout_dir,
LAYOUT_DIR_DESCRIPTION,
"layout_dir {:?}",
"// layout_dir /path/to/my/layout_dir"
);
push_option!(
theme_dir,
THEME_DIR_DESCRIPTION,
"theme_dir {:?}",
"// theme_dir \"/path/to/my/theme_dir\""
);
options_kdl
}
fn env_yaml_to_env_kdl(env_yaml: &OldEnvironmentVariablesFromYaml) -> String {
let mut env_kdl = String::new();
let mut env_vars: Vec<(String, String)> = env_yaml
.env
.iter()
.map(|(name, val)| (name.clone(), val.clone()))
.collect();
env_vars.sort_unstable();
env_kdl.push_str("env {\n");
for (name, val) in env_vars {
env_kdl.push_str(&format!(" {} \"{}\"\n", name, val));
}
env_kdl.push_str("}\n");
env_kdl
}
fn plugins_yaml_to_plugins_kdl(plugins_yaml_to_plugins_kdl: &OldPluginsConfigFromYaml) -> String {
let mut plugins_kdl = String::new();
if !&plugins_yaml_to_plugins_kdl.0.is_empty() {
plugins_kdl.push_str("\n");
plugins_kdl.push_str("plugins {\n")
}
for plugin_config in &plugins_yaml_to_plugins_kdl.0 {
if plugin_config._allow_exec_host_cmd {
plugins_kdl.push_str(&format!(
" {} {{ path {:?}; _allow_exec_host_cmd true; }}\n",
plugin_config.tag.0, plugin_config.path
));
} else {
plugins_kdl.push_str(&format!(
" {} {{ path {:?}; }}\n",
plugin_config.tag.0, plugin_config.path
));
}
}
if !&plugins_yaml_to_plugins_kdl.0.is_empty() {
plugins_kdl.push_str("}\n")
}
plugins_kdl
}
fn ui_config_yaml_to_ui_config_kdl(ui_config_yaml: &OldUiConfigFromYaml) -> String {
let mut kdl_ui_config = String::new();
if ui_config_yaml.pane_frames.rounded_corners {
kdl_ui_config.push_str("\n");
kdl_ui_config.push_str("ui {\n");
kdl_ui_config.push_str(" pane_frames {\n");
kdl_ui_config.push_str(" rounded_corners true\n");
kdl_ui_config.push_str(" }\n");
kdl_ui_config.push_str("}\n");
} else {
// I'm not sure this is a thing, but since it's possible, why not?
kdl_ui_config.push_str("\n");
kdl_ui_config.push_str("ui {\n");
kdl_ui_config.push_str(" pane_frames {\n");
kdl_ui_config.push_str(" rounded_corners false\n");
kdl_ui_config.push_str(" }\n");
kdl_ui_config.push_str("}\n");
}
kdl_ui_config
}
fn theme_config_yaml_to_theme_config_kdl(
theme_config_yaml: &OldThemesFromYamlIntermediate,
) -> String {
macro_rules! theme_color {
($theme:ident, $color:ident, $color_name:expr, $kdl_theme_config:expr) => {
match $theme.palette.$color {
OldPaletteColorFromYaml::Rgb((r, g, b)) => {
$kdl_theme_config
.push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b));
},
OldPaletteColorFromYaml::EightBit(eight_bit_color) => {
$kdl_theme_config
.push_str(&format!(" {} {}\n", $color_name, eight_bit_color));
},
OldPaletteColorFromYaml::Hex(OldHexColor(r, g, b)) => {
$kdl_theme_config
.push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b));
},
}
};
}
let mut kdl_theme_config = String::new();
if !theme_config_yaml.0.is_empty() {
kdl_theme_config.push_str("themes {\n")
}
let mut themes: Vec<(String, OldTheme)> = theme_config_yaml
.0
.iter()
.map(|(theme_name, theme)| (theme_name.clone(), theme.clone()))
.collect();
themes.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
for (theme_name, theme) in themes {
kdl_theme_config.push_str(&format!(" {} {{\n", theme_name));
theme_color!(theme, fg, "fg", kdl_theme_config);
theme_color!(theme, bg, "bg", kdl_theme_config);
theme_color!(theme, black, "black", kdl_theme_config);
theme_color!(theme, red, "red", kdl_theme_config);
theme_color!(theme, green, "green", kdl_theme_config);
theme_color!(theme, yellow, "yellow", kdl_theme_config);
theme_color!(theme, blue, "blue", kdl_theme_config);
theme_color!(theme, magenta, "magenta", kdl_theme_config);
theme_color!(theme, cyan, "cyan", kdl_theme_config);
theme_color!(theme, white, "white", kdl_theme_config);
theme_color!(theme, orange, "orange", kdl_theme_config);
kdl_theme_config.push_str(" }\n");
}
if !theme_config_yaml.0.is_empty() {
kdl_theme_config.push_str("}\n")
}
kdl_theme_config
}
fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String {
let mut kdl_keybinds = String::new();
let modes = vec![
// mode sort order
OldInputMode::Normal,
OldInputMode::Locked,
OldInputMode::Pane,
OldInputMode::Tab,
OldInputMode::Resize,
OldInputMode::Move,
OldInputMode::Scroll,
OldInputMode::Session,
OldInputMode::Search,
OldInputMode::EnterSearch,
OldInputMode::RenameTab,
OldInputMode::RenamePane,
OldInputMode::Prompt,
OldInputMode::Tmux,
];
// title and global unbinds / clear-defaults
match &keybinds_yaml.unbind {
OldUnbind::Keys(keys_to_unbind) => {
kdl_keybinds.push_str("keybinds {\n");
let key_string: String = keys_to_unbind
.iter()
.map(|k| format!("\"{}\"", k))
.collect::<Vec<String>>()
.join(" ");
kdl_keybinds.push_str(&format!(" unbind {}\n", key_string));
},
OldUnbind::All(should_unbind_all_defaults) => {
if *should_unbind_all_defaults {
kdl_keybinds.push_str("keybinds clear-defaults=true {\n");
} else {
kdl_keybinds.push_str("keybinds {\n");
}
},
}
for mode in modes {
if let Some(mode_keybinds) = keybinds_yaml.keybinds.get(&mode) {
let mut should_clear_mode_defaults = false;
let mut kdl_mode_keybinds = String::new();
for key_action_unbind in mode_keybinds {
match key_action_unbind {
OldKeyActionUnbind::KeyAction(key_action) => {
let keys = &key_action.key;
let actions = &key_action.action;
let key_string: String = keys
.iter()
.map(|k| {
if k == &OldKey::Char('\\') {
format!("r\"{}\"", k)
} else {
format!("\"{}\"", k)
}
})
.collect::<Vec<String>>()
.join(" ");
let actions_string: String = actions
.iter()
.map(|a| format!("{};", a))
.collect::<Vec<String>>()
.join(" ");
kdl_mode_keybinds.push_str(&format!(
" bind {} {{ {} }}\n",
key_string, actions_string
));
},
OldKeyActionUnbind::Unbind(unbind) => match &unbind.unbind {
OldUnbind::Keys(keys_to_unbind) => {
let key_string: String = keys_to_unbind
.iter()
.map(|k| format!("\"{}\"", k))
.collect::<Vec<String>>()
.join(" ");
kdl_mode_keybinds.push_str(&format!(" unbind {}\n", key_string));
},
OldUnbind::All(unbind_all) => {
if *unbind_all {
should_clear_mode_defaults = true;
}
},
},
}
}
if should_clear_mode_defaults {
kdl_keybinds.push_str(&format!(" {} clear-defaults=true {{\n", mode));
} else {
kdl_keybinds.push_str(&format!(" {} {{\n", mode));
}
kdl_keybinds.push_str(&kdl_mode_keybinds);
kdl_keybinds.push_str(" }\n");
}
}
kdl_keybinds.push_str("}\n");
kdl_keybinds
}
pub fn config_yaml_to_config_kdl(
raw_yaml_config: &str,
no_comments: bool,
) -> Result<String, String> {
// returns the raw kdl config
let config_from_yaml: OldConfigFromYaml = serde_yaml::from_str(raw_yaml_config)
.map_err(|e| format!("Failed to parse yaml: {:?}", e))?;
let mut kdl_config = String::new();
if let Some(old_config_keybinds) = config_from_yaml.keybinds.as_ref() {
kdl_config.push_str(&keybinds_yaml_to_keybinds_kdl(old_config_keybinds));
}
if let Some(old_config_options) = config_from_yaml.options.as_ref() {
kdl_config.push_str(&options_yaml_to_options_kdl(
old_config_options,
no_comments,
));
}
if let Some(old_config_env_variables) = config_from_yaml.env.as_ref() {
kdl_config.push_str(&env_yaml_to_env_kdl(old_config_env_variables));
}
kdl_config.push_str(&plugins_yaml_to_plugins_kdl(&config_from_yaml.plugins));
if let Some(old_ui_config) = config_from_yaml.ui.as_ref() {
kdl_config.push_str(&ui_config_yaml_to_ui_config_kdl(old_ui_config));
}
if let Some(old_themes_config) = config_from_yaml.themes.as_ref() {
kdl_config.push_str(&theme_config_yaml_to_theme_config_kdl(old_themes_config));
}
Ok(kdl_config)
}
#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq)]
pub struct OldConfigFromYaml {
#[serde(flatten)]
pub options: Option<OldOptions>,
pub keybinds: Option<OldKeybindsFromYaml>,
pub themes: Option<OldThemesFromYamlIntermediate>,
#[serde(flatten)]
pub env: Option<OldEnvironmentVariablesFromYaml>,
#[serde(default)]
pub plugins: OldPluginsConfigFromYaml,
pub ui: Option<OldUiConfigFromYaml>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct OldKeybindsFromYaml {
#[serde(flatten)]
keybinds: HashMap<OldInputMode, Vec<OldKeyActionUnbind>>,
#[serde(default)]
unbind: OldUnbind,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(untagged)]
enum OldUnbind {
// This is the correct order, don't rearrange!
// Suspected Bug in the untagged macro.
// 1. Keys
Keys(Vec<OldKey>),
// 2. All
All(bool),
}
impl Default for OldUnbind {
fn default() -> OldUnbind {
OldUnbind::All(false)
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
enum OldKeyActionUnbind {
KeyAction(OldKeyActionFromYaml),
Unbind(OldUnbindFromYaml),
}
/// Intermediate struct used for deserialisation
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct OldKeyActionFromYaml {
action: Vec<OldAction>,
key: Vec<OldKey>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct OldUnbindFromYaml {
unbind: OldUnbind,
}
/// Main configuration.
#[derive(Debug, Clone, PartialEq, Deserialize)]
struct OldConfig {
pub keybinds: OldKeybinds,
pub options: OldOptions,
pub themes: Option<OldThemesFromYamlIntermediate>,
pub plugins: OldPluginsConfig,
pub ui: Option<OldUiConfigFromYaml>,
pub env: OldEnvironmentVariablesFromYaml,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct OldKeybinds(HashMap<OldInputMode, OldModeKeybinds>);
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct OldModeKeybinds(BTreeMap<OldKey, Vec<OldAction>>);
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct OldThemesFromYamlIntermediate(HashMap<String, OldTheme>);
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
struct OldPaletteFromYaml {
pub fg: OldPaletteColorFromYaml,
pub bg: OldPaletteColorFromYaml,
pub black: OldPaletteColorFromYaml,
pub red: OldPaletteColorFromYaml,
pub green: OldPaletteColorFromYaml,
pub yellow: OldPaletteColorFromYaml,
pub blue: OldPaletteColorFromYaml,
pub magenta: OldPaletteColorFromYaml,
pub cyan: OldPaletteColorFromYaml,
pub white: OldPaletteColorFromYaml,
pub orange: OldPaletteColorFromYaml,
}
/// Intermediate deserialization enum
// This is here in order to make the untagged enum work
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(untagged)]
enum OldPaletteColorFromYaml {
Rgb((u8, u8, u8)),
EightBit(u8),
Hex(OldHexColor),
}
impl From<OldHexColor> for (u8, u8, u8) {
fn from(e: OldHexColor) -> (u8, u8, u8) {
let OldHexColor(r, g, b) = e;
(r, g, b)
}
}
struct OldHexColorVisitor();
impl<'de> Visitor<'de> for OldHexColorVisitor {
type Value = OldHexColor;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a hex color in the format #RGB or #RRGGBB")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
if let Some(stripped) = s.strip_prefix('#') {
return self.visit_str(stripped);
}
if s.len() == 3 {
Ok(OldHexColor(
u8::from_str_radix(&s[0..1], 16).map_err(E::custom)? * 0x11,
u8::from_str_radix(&s[1..2], 16).map_err(E::custom)? * 0x11,
u8::from_str_radix(&s[2..3], 16).map_err(E::custom)? * 0x11,
))
} else if s.len() == 6 {
Ok(OldHexColor(
u8::from_str_radix(&s[0..2], 16).map_err(E::custom)?,
u8::from_str_radix(&s[2..4], 16).map_err(E::custom)?,
u8::from_str_radix(&s[4..6], 16).map_err(E::custom)?,
))
} else {
Err(Error::custom(
"Hex color must be of form \"#RGB\" or \"#RRGGBB\"",
))
}
}
}
impl<'de> Deserialize<'de> for OldHexColor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(OldHexColorVisitor())
}
}
impl Default for OldPaletteColorFromYaml {
fn default() -> Self {
OldPaletteColorFromYaml::EightBit(0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
struct OldHexColor(u8, u8, u8);
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
struct OldTheme {
#[serde(flatten)]
palette: OldPaletteFromYaml,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
pub struct OldUiConfigFromYaml {
pub pane_frames: OldFrameConfigFromYaml,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
pub struct OldFrameConfigFromYaml {
pub rounded_corners: bool,
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct OldEnvironmentVariablesFromYaml {
env: HashMap<String, String>,
}
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct OldPluginsConfigFromYaml(Vec<OldPluginConfigFromYaml>);
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
struct OldPluginConfigFromYaml {
pub path: PathBuf,
pub tag: OldPluginTag,
#[serde(default)]
pub run: OldPluginTypeFromYaml,
#[serde(default)]
pub config: serde_yaml::Value,
#[serde(default)]
pub _allow_exec_host_cmd: bool,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
enum OldPluginTypeFromYaml {
Headless,
Pane,
}
impl Default for OldPluginTypeFromYaml {
fn default() -> Self {
Self::Pane
}
}
/// Tag used to identify the plugin in layout and config yaml files
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
struct OldPluginTag(String);
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct OldPluginsConfig(HashMap<OldPluginTag, OldPluginConfig>);
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct OldPluginConfig {
/// Path of the plugin, see resolve_wasm_bytes for resolution semantics
pub path: PathBuf,
/// Plugin type
pub run: OldPluginType,
/// Allow command execution from plugin
pub _allow_exec_host_cmd: bool,
/// Original location of the
pub location: OldRunPluginLocation,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
enum OldRunPluginLocation {
File(PathBuf),
Zellij(OldPluginTag),
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
enum OldPluginType {
/// Starts immediately when Zellij is started and runs without a visible pane
Headless,
/// Runs once per pane declared inside a layout file
Pane(Option<usize>), // tab_index
}
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum OldOnForceClose {
#[serde(alias = "quit")]
Quit,
#[serde(alias = "detach")]
Detach,
}
impl std::fmt::Display for OldOnForceClose {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Quit => write!(f, "quit"),
Self::Detach => write!(f, "detach"),
}
}
}
impl Default for OldOnForceClose {
fn default() -> Self {
Self::Detach
}
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
pub enum OldClipboard {
#[serde(alias = "system")]
System,
#[serde(alias = "primary")]
Primary,
}
impl std::fmt::Display for OldClipboard {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::System => write!(f, "system"),
Self::Primary => write!(f, "primary"),
}
}
}
impl Default for OldClipboard {
fn default() -> Self {
Self::System
}
}
#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize)]
pub struct OldOptions {
#[serde(default)]
pub simplified_ui: Option<bool>,
pub theme: Option<String>,
pub default_mode: Option<OldInputMode>,
pub default_shell: Option<PathBuf>,
pub default_layout: Option<PathBuf>,
pub layout_dir: Option<PathBuf>,
pub theme_dir: Option<PathBuf>,
#[serde(default)]
pub mouse_mode: Option<bool>,
#[serde(default)]
pub pane_frames: Option<bool>,
#[serde(default)]
pub mirror_session: Option<bool>,
pub on_force_close: Option<OldOnForceClose>,
pub scroll_buffer_size: Option<usize>,
#[serde(default)]
pub copy_command: Option<String>,
#[serde(default)]
pub copy_clipboard: Option<OldClipboard>,
#[serde(default)]
pub copy_on_select: Option<bool>,
pub scrollback_editor: Option<PathBuf>,
}
/// Describes the different input modes, which change the way that keystrokes will be interpreted.
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
pub enum OldInputMode {
/// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading
/// to other modes
#[serde(alias = "normal")]
Normal,
/// In `Locked` mode, input is always written to the terminal and all shortcuts are disabled
/// except the one leading back to normal mode
#[serde(alias = "locked")]
Locked,
/// `Resize` mode allows resizing the different existing panes.
#[serde(alias = "resize")]
Resize,
/// `Pane` mode allows creating and closing panes, as well as moving between them.
#[serde(alias = "pane")]
Pane,
/// `Tab` mode allows creating and closing tabs, as well as moving between them.
#[serde(alias = "tab")]
Tab,
/// `Scroll` mode allows scrolling up and down within a pane.
#[serde(alias = "scroll")]
Scroll,
/// `EnterSearch` mode allows for typing in the needle for a search in the scroll buffer of a pane.
#[serde(alias = "entersearch")]
EnterSearch,
/// `Search` mode allows for searching a term in a pane (superset of `Scroll`).
#[serde(alias = "search")]
Search,
/// `RenameTab` mode allows assigning a new name to a tab.
#[serde(alias = "renametab")]
RenameTab,
/// `RenamePane` mode allows assigning a new name to a pane.
#[serde(alias = "renamepane")]
RenamePane,
/// `Session` mode allows detaching sessions
#[serde(alias = "session")]
Session,
/// `Move` mode allows moving the different existing panes within a tab
#[serde(alias = "move")]
Move,
/// `Prompt` mode allows interacting with active prompts.
#[serde(alias = "prompt")]
Prompt,
/// `Tmux` mode allows for basic tmux keybindings functionality
#[serde(alias = "tmux")]
Tmux,
}
impl std::fmt::Display for OldInputMode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Normal => write!(f, "normal"),
Self::Locked => write!(f, "locked"),
Self::Resize => write!(f, "resize"),
Self::Pane => write!(f, "pane"),
Self::Tab => write!(f, "tab"),
Self::Scroll => write!(f, "scroll"),
Self::EnterSearch => write!(f, "entersearch"),
Self::Search => write!(f, "search"),
Self::RenameTab => write!(f, "RenameTab"),
Self::RenamePane => write!(f, "RenamePane"),
Self::Session => write!(f, "session"),
Self::Move => write!(f, "move"),
Self::Prompt => write!(f, "prompt"),
Self::Tmux => write!(f, "tmux"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
enum OldKey {
PageDown,
PageUp,
Left,
Down,
Up,
Right,
Home,
End,
Backspace,
Delete,
Insert,
F(u8),
Char(char),
Alt(OldCharOrArrow),
Ctrl(char),
BackTab,
Null,
Esc,
}
impl std::fmt::Display for OldKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::PageDown => write!(f, "PageDown"),
Self::PageUp => write!(f, "PageUp"),
Self::Left => write!(f, "Left"),
Self::Down => write!(f, "Down"),
Self::Up => write!(f, "Up"),
Self::Right => write!(f, "Right"),
Self::Home => write!(f, "Home"),
Self::End => write!(f, "End"),
Self::Backspace => write!(f, "Backspace"),
Self::Delete => write!(f, "Delete"),
Self::Insert => write!(f, "Insert"),
Self::F(index) => write!(f, "F{}", index),
Self::Char(c) => match c {
'\n' => write!(f, "Enter"),
'\t' => write!(f, "Tab"),
'\"' => write!(f, "\\\""), // make sure it is escaped because otherwise it will be
// seen as a KDL string starter/terminator
' ' => write!(f, "Space"),
_ => write!(f, "{}", c),
},
Self::Alt(char_or_arrow) => match char_or_arrow {
OldCharOrArrow::Char(c) => write!(f, "Alt {}", c),
OldCharOrArrow::Direction(direction) => match direction {
OldDirection::Left => write!(f, "Alt Left"),
OldDirection::Right => write!(f, "Alt Right"),
OldDirection::Up => write!(f, "Alt Up"),
OldDirection::Down => write!(f, "Alt Down"),
},
},
Self::Ctrl(c) => write!(f, "Ctrl {}", c),
Self::BackTab => write!(f, "Tab"),
Self::Null => write!(f, "Null"),
Self::Esc => write!(f, "Esc"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
#[serde(untagged)]
enum OldCharOrArrow {
Char(char),
Direction(OldDirection),
}
/// The four directions (left, right, up, down).
#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
enum OldDirection {
Left,
Right,
Up,
Down,
}
impl std::fmt::Display for OldDirection {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Left => write!(f, "Left"),
Self::Right => write!(f, "Right"),
Self::Up => write!(f, "Up"),
Self::Down => write!(f, "Down"),
}
}
}
impl Default for OldDirection {
fn default() -> Self {
OldDirection::Left
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
enum OldAction {
Quit,
Write(Vec<u8>),
WriteChars(String),
SwitchToMode(OldInputMode),
Resize(OldResizeDirection),
FocusNextPane,
FocusPreviousPane,
SwitchFocus,
MoveFocus(OldDirection),
MoveFocusOrTab(OldDirection),
MovePane(Option<OldDirection>),
DumpScreen(String),
EditScrollback,
ScrollUp,
ScrollUpAt(OldPosition),
ScrollDown,
ScrollDownAt(OldPosition),
ScrollToBottom,
PageScrollUp,
PageScrollDown,
HalfPageScrollUp,
HalfPageScrollDown,
ToggleFocusFullscreen,
TogglePaneFrames,
ToggleActiveSyncTab,
NewPane(Option<OldDirection>),
TogglePaneEmbedOrFloating,
ToggleFloatingPanes,
CloseFocus,
PaneNameInput(Vec<u8>),
UndoRenamePane,
NewTab(Option<OldTabLayout>),
NoOp,
GoToNextTab,
GoToPreviousTab,
CloseTab,
GoToTab(u32),
ToggleTab,
TabNameInput(Vec<u8>),
UndoRenameTab,
Run(OldRunCommandAction),
Detach,
LeftClick(OldPosition),
RightClick(OldPosition),
MiddleClick(OldPosition),
LeftMouseRelease(OldPosition),
RightMouseRelease(OldPosition),
MiddleMouseRelease(OldPosition),
MouseHoldLeft(OldPosition),
MouseHoldRight(OldPosition),
MouseHoldMiddle(OldPosition),
Copy,
Confirm,
Deny,
SkipConfirm(Box<OldAction>),
SearchInput(Vec<u8>),
Search(OldSearchDirection),
SearchToggleOption(OldSearchOption),
}
impl std::fmt::Display for OldAction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Quit => write!(f, "Quit"),
Self::Write(bytes) => write!(
f,
"Write {}",
bytes
.iter()
.map(|c| format!("{}", *c))
.collect::<Vec<String>>()
.join(" ")
),
Self::WriteChars(chars) => write!(f, "WriteChars \"{}\"", chars),
Self::SwitchToMode(input_mode) => write!(f, "SwitchToMode \"{}\"", input_mode),
Self::Resize(resize_direction) => write!(f, "Resize \"{}\"", resize_direction),
Self::FocusNextPane => write!(f, "FocusNextPane"),
Self::FocusPreviousPane => write!(f, "FocusPreviousPane"),
Self::SwitchFocus => write!(f, "SwitchFocus"),
Self::MoveFocus(direction) => write!(f, "MoveFocus \"{}\"", direction),
Self::MoveFocusOrTab(direction) => write!(f, "MoveFocusOrTab \"{}\"", direction),
Self::MovePane(direction) => match direction {
Some(direction) => write!(f, "MovePane \"{}\"", direction),
None => write!(f, "MovePane"),
},
Self::DumpScreen(file) => write!(f, "DumpScreen \"{}\"", file),
Self::EditScrollback => write!(f, "EditScrollback"),
Self::ScrollUp => write!(f, "ScrollUp"),
Self::ScrollDown => write!(f, "ScrollDown"),
Self::ScrollToBottom => write!(f, "ScrollToBottom"),
Self::PageScrollUp => write!(f, "PageScrollUp"),
Self::PageScrollDown => write!(f, "PageScrollDown"),
Self::HalfPageScrollUp => write!(f, "HalfPageScrollUp"),
Self::HalfPageScrollDown => write!(f, "HalfPageScrollDown"),
Self::ToggleFocusFullscreen => write!(f, "ToggleFocusFullscreen"),
Self::TogglePaneFrames => write!(f, "TogglePaneFrames"),
Self::ToggleActiveSyncTab => write!(f, "ToggleActiveSyncTab"),
Self::NewPane(direction) => match direction {
Some(direction) => write!(f, "NewPane \"{}\"", direction),
None => write!(f, "NewPane"),
},
Self::TogglePaneEmbedOrFloating => write!(f, "TogglePaneEmbedOrFloating"),
Self::ToggleFloatingPanes => write!(f, "ToggleFloatingPanes"),
Self::CloseFocus => write!(f, "CloseFocus"),
Self::PaneNameInput(bytes) => write!(
f,
"PaneNameInput {}",
bytes
.iter()
.map(|c| format!("{}", *c))
.collect::<Vec<String>>()
.join(" ")
),
Self::UndoRenamePane => write!(f, "UndoRenamePane"),
Self::NewTab(_) => write!(f, "NewTab"),
Self::NoOp => write!(f, "NoOp"),
Self::GoToNextTab => write!(f, "GoToNextTab"),
Self::GoToPreviousTab => write!(f, "GoToPreviousTab"),
Self::CloseTab => write!(f, "CloseTab"),
Self::GoToTab(index) => write!(f, "GoToTab {}", index),
Self::ToggleTab => write!(f, "ToggleTab"),
// Self::TabNameInput(bytes) => write!(f, "TabNameInput {}", format!("{}", bytes.iter().map(|c| format!("{}", *c)).collect::<Vec<String>>().join(" "))),
Self::TabNameInput(bytes) => write!(
f,
"TabNameInput {}",
bytes
.iter()
.map(|c| format!("{}", *c))
.collect::<Vec<String>>()
.join(" ")
),
Self::UndoRenameTab => write!(f, "UndoRenameTab"),
Self::Run(run_command_action) => {
let mut run_block_serialized = format!("Run {:?}", run_command_action.command);
for arg in &run_command_action.args {
run_block_serialized.push_str(&format!(" \"{}\"", arg));
}
match (&run_command_action.cwd, run_command_action.direction) {
(Some(cwd), Some(direction)) => {
run_block_serialized.push_str(&format!(
"{{ cwd {:?}; direction \"{}\"; }}",
cwd, direction
));
},
(None, Some(direction)) => {
run_block_serialized
.push_str(&format!("{{ direction \"{}\"; }}", direction));
},
(Some(cwd), None) => {
run_block_serialized.push_str(&format!("{{ cwd {:?}; }}", cwd));
},
(None, None) => {},
}
write!(f, "{}", run_block_serialized)
},
Self::Detach => write!(f, "Detach"),
Self::Copy => write!(f, "Copy"),
Self::Confirm => write!(f, "Confirm"),
Self::Deny => write!(f, "Deny"),
Self::SearchInput(bytes) => write!(
f,
"SearchInput {}",
bytes
.iter()
.map(|c| format!("{}", *c))
.collect::<Vec<String>>()
.join(" ")
),
Self::Search(direction) => write!(f, "Search \"{}\"", direction),
Self::SearchToggleOption(option) => write!(f, "SearchToggleOption \"{}\"", option),
_ => Err(std::fmt::Error),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
enum OldSearchDirection {
Down,
Up,
}
impl std::fmt::Display for OldSearchDirection {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Down => write!(f, "Down"),
Self::Up => write!(f, "Up"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
enum OldSearchOption {
CaseSensitivity,
WholeWord,
Wrap,
}
impl std::fmt::Display for OldSearchOption {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::CaseSensitivity => write!(f, "CaseSensitivity"),
Self::WholeWord => write!(f, "WholeWord"),
Self::Wrap => write!(f, "Wrap"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
enum OldResizeDirection {
Left,
Right,
Up,
Down,
Increase,
Decrease,
}
impl std::fmt::Display for OldResizeDirection {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Left => write!(f, "Left"),
Self::Right => write!(f, "Right"),
Self::Up => write!(f, "Up"),
Self::Down => write!(f, "Down"),
Self::Increase => write!(f, "Increase"),
Self::Decrease => write!(f, "Decrease"),
}
}
}
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Deserialize, Serialize)]
struct OldPosition {
pub line: OldLine,
pub column: OldColumn,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd)]
struct OldLine(pub isize);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd)]
struct OldColumn(pub usize);
#[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)]
struct OldRunCommandAction {
#[serde(rename = "cmd")]
pub command: PathBuf,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub cwd: Option<PathBuf>,
#[serde(default)]
pub direction: Option<OldDirection>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
struct OldTabLayout {
#[serde(default)]
pub direction: OldDirection,
pub pane_name: Option<String>,
#[serde(default)]
pub borderless: bool,
#[serde(default)]
pub parts: Vec<OldTabLayout>,
pub split_size: Option<OldSplitSize>,
#[serde(default)]
pub name: String,
pub focus: Option<bool>,
pub run: Option<OldRunFromYaml>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
enum OldSplitSize {
#[serde(alias = "percent")]
Percent(u64), // 1 to 100
#[serde(alias = "fixed")]
Fixed(usize), // An absolute number of columns or rows
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
enum OldRunFromYaml {
#[serde(rename = "plugin")]
Plugin(OldRunPluginFromYaml),
#[serde(rename = "command")]
Command(OldRunCommand),
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
struct OldRunPluginFromYaml {
#[serde(default)]
pub _allow_exec_host_cmd: bool,
pub location: Url,
}
#[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)]
pub struct OldRunCommand {
#[serde(alias = "cmd")]
pub command: PathBuf,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub cwd: Option<PathBuf>,
}
// The unit test location.
#[path = "./unit/convert_config_tests.rs"]
#[cfg(test)]
mod convert_config_test;