aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-08-16 14:00:01 +0200
committerGitHub <noreply@github.com>2024-08-16 14:00:01 +0200
commit056537d3eef06cddc60dc13b715a41766bb1fe9d (patch)
treeff7e107e992e4c98ac60ac88da7215cb0beb668a /zellij-utils/src
parentdocs(changelog): CommandPaneReRun plugin event (diff)
downloadsl0ppij-056537d3eef06cddc60dc13b715a41766bb1fe9d.tar.gz
sl0ppij-056537d3eef06cddc60dc13b715a41766bb1fe9d.tar.xz
sl0ppij-056537d3eef06cddc60dc13b715a41766bb1fe9d.zip
feat(ux): first run setup-wizard (#3556)
* separate saved/runtime structure, kind of working * serializing config * work * work * save config through the configuration screen * work * startup wizard * style(code): cleanups * fix(session): reload config from disk when switching sessions * style(fmt): rustfmt * fix(config): propagate cli config options to screen * style(fmt): rustfmt
Diffstat (limited to 'zellij-utils/src')
-rw-r--r--zellij-utils/src/data.rs6
-rw-r--r--zellij-utils/src/envs.rs3
-rw-r--r--zellij-utils/src/errors.rs4
-rw-r--r--zellij-utils/src/input/config.rs165
-rw-r--r--zellij-utils/src/input/layout.rs19
-rw-r--r--zellij-utils/src/input/theme.rs6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap2
-rw-r--r--zellij-utils/src/ipc.rs13
-rw-r--r--zellij-utils/src/kdl/mod.rs2667
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap240
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap409
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string.snap33
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap175
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_some_options.snap7
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_without_options.snap6
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__env_vars_to_string.snap11
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string.snap11
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap130
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_actions.snap11
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_multiline_actions.snap25
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_shared_modes.snap17
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_without_clearing_defaults.snap11
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string.snap18
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string_with_file_and_web.snap11
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap20
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap20
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap20
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap20
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap33
-rw-r--r--zellij-utils/src/kdl/snapshots/zellij_utils__kdl__ui_config_to_string.snap11
-rw-r--r--zellij-utils/src/plugin_api/event.proto6
-rw-r--r--zellij-utils/src/plugin_api/event.rs16
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto7
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs18
-rw-r--r--zellij-utils/src/setup.rs3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap5
36 files changed, 4153 insertions, 26 deletions
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index f03c01cf..59653e78 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -120,7 +120,7 @@ impl fmt::Display for KeyWithModifier {
.iter()
.map(|m| m.to_string())
.collect::<Vec<_>>()
- .join("-"),
+ .join(" "),
self.bare_key
)
}
@@ -920,6 +920,7 @@ pub enum Event {
EditPaneOpened(u32, Context), // u32 - terminal_pane_id
EditPaneExited(u32, Option<i32>, Context), // u32 - terminal_pane_id, Option<i32> - exit code
CommandPaneReRun(u32, Context), // u32 - terminal_pane_id, Option<i32> -
+ FailedToWriteConfigToDisk(Option<String>), // String -> the file path we failed to write
}
#[derive(
@@ -1799,7 +1800,8 @@ pub enum PluginCommand {
DumpSessionLayout,
CloseSelf,
NewTabsWithLayoutInfo(LayoutInfo),
- Reconfigure(String), // String -> stringified configuration
+ Reconfigure(String, bool), // String -> stringified configuration, bool -> save configuration
+ // file to disk
HidePaneWithId(PaneId),
ShowPaneWithId(PaneId, bool), // bool -> should_float_if_hidden
OpenCommandPaneBackground(CommandToRun, Context),
diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs
index c7aff980..bda884a9 100644
--- a/zellij-utils/src/envs.rs
+++ b/zellij-utils/src/envs.rs
@@ -64,4 +64,7 @@ impl EnvironmentVariables {
set_var(k, v);
}
}
+ pub fn inner(&self) -> &HashMap<String, String> {
+ &self.env
+ }
}
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 94417e00..21b9294e 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -411,6 +411,7 @@ pub enum PluginContext {
DumpLayoutToPlugin,
ListClientsMetadata,
Reconfigure,
+ FailedToWriteConfigToDisk,
}
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
@@ -434,6 +435,7 @@ pub enum ClientContext {
UnblockCliPipeInput,
CliPipeOutput,
QueryTerminalSize,
+ WriteConfigToDisk,
}
/// Stack call representations corresponding to the different types of [`ServerInstruction`]s.
@@ -460,6 +462,8 @@ pub enum ServerContext {
ChangeMode,
ChangeModeForAllClients,
Reconfigure,
+ ConfigWrittenToDisk,
+ FailedToWriteConfigToDisk,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs
index 56c6f338..92174eb2 100644
--- a/zellij-utils/src/input/config.rs
+++ b/zellij-utils/src/input/config.rs
@@ -1,5 +1,6 @@
use crate::data::Palette;
use miette::{Diagnostic, LabeledSpan, NamedSource, SourceCode};
+use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::{self, Read};
use std::path::PathBuf;
@@ -20,7 +21,7 @@ const DEFAULT_CONFIG_FILE_NAME: &str = "config.kdl";
type ConfigResult = Result<Config, ConfigError>;
/// Main configuration.
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Config {
pub keybinds: Keybinds,
pub options: Options,
@@ -233,6 +234,164 @@ impl Config {
self.env = self.env.merge(other.env);
Ok(())
}
+ fn config_file_path(opts: &CliArgs) -> Option<PathBuf> {
+ opts.config_dir
+ .clone()
+ .or_else(home::find_default_config_dir)
+ .map(|config_dir| config_dir.join(DEFAULT_CONFIG_FILE_NAME))
+ }
+ pub fn write_config_to_disk(config: String, opts: &CliArgs) -> Result<Config, Option<PathBuf>> {
+ // if we fail, try to return the PathBuf of the file we were not able to write to
+ Config::from_kdl(&config, None)
+ .map_err(|e| {
+ log::error!("Failed to parse config: {}", e);
+ None
+ })
+ .and_then(|parsed_config| {
+ let backed_up_file_name = Config::backup_current_config(&opts)?;
+ let config_file_path = Config::config_file_path(&opts).ok_or_else(|| {
+ log::error!("Config file path not found");
+ None
+ })?;
+ let config = match backed_up_file_name {
+ Some(backed_up_file_name) => {
+ format!(
+ "{}{}",
+ Config::autogen_config_message(backed_up_file_name),
+ config
+ )
+ },
+ None => config,
+ };
+ std::fs::write(&config_file_path, config.as_bytes()).map_err(|e| {
+ log::error!("Failed to write config: {}", e);
+ Some(config_file_path.clone())
+ })?;
+ let written_config = std::fs::read_to_string(&config_file_path).map_err(|e| {
+ log::error!("Failed to read written config: {}", e);
+ Some(config_file_path.clone())
+ })?;
+ let parsed_written_config =
+ Config::from_kdl(&written_config, None).map_err(|e| {
+ log::error!("Failed to parse written config: {}", e);
+ None
+ })?;
+ if parsed_written_config == parsed_config {
+ Ok(parsed_config)
+ } else {
+ log::error!("Configuration corrupted when writing to disk");
+ Err(Some(config_file_path))
+ }
+ })
+ }
+ // returns true if the config was not previouly written to disk and we successfully wrote it
+ pub fn write_config_to_disk_if_it_does_not_exist(config: String, opts: &CliArgs) -> bool {
+ match Config::config_file_path(opts) {
+ Some(config_file_path) => {
+ if config_file_path.exists() {
+ false
+ } else {
+ if let Err(e) = std::fs::write(&config_file_path, config.as_bytes()) {
+ log::error!("Failed to write config to disk: {}", e);
+ return false;
+ }
+ match std::fs::read_to_string(&config_file_path) {
+ Ok(written_config) => written_config == config,
+ Err(e) => {
+ log::error!("Failed to read written config: {}", e);
+ false
+ },
+ }
+ }
+ },
+ None => false,
+ }
+ }
+ fn find_free_backup_file_name(config_file_path: &PathBuf) -> Option<PathBuf> {
+ let mut backup_config_path = None;
+ let config_file_name = config_file_path
+ .file_name()
+ .and_then(|f| f.to_str())
+ .unwrap_or_else(|| DEFAULT_CONFIG_FILE_NAME);
+ for i in 0..100 {
+ let new_file_name = if i == 0 {
+ format!("{}.bak", config_file_name)
+ } else {
+ format!("{}.bak.{}", config_file_name, i)
+ };
+ let mut potential_config_path = config_file_path.clone();
+ potential_config_path.set_file_name(new_file_name);
+ if !potential_config_path.exists() {
+ backup_config_path = Some(potential_config_path);
+ break;
+ }
+ }
+ backup_config_path
+ }
+ fn backup_config_with_written_content_confirmation(
+ current_config: &str,
+ current_config_file_path: &PathBuf,
+ backup_config_path: &PathBuf,
+ ) -> bool {
+ let _ = std::fs::copy(current_config_file_path, &backup_config_path);
+ match std::fs::read_to_string(&backup_config_path) {
+ Ok(backed_up_config) => current_config == &backed_up_config,
+ Err(e) => {
+ log::error!(
+ "Failed to back up config file {}: {:?}",
+ backup_config_path.display(),
+ e
+ );
+ false
+ },
+ }
+ }
+ fn backup_current_config(opts: &CliArgs) -> Result<Option<PathBuf>, Option<PathBuf>> {
+ // if we fail, try to return the PathBuf of the file we were not able to write to
+ if let Some(config_file_path) = Config::config_file_path(&opts) {
+ match std::fs::read_to_string(&config_file_path) {
+ Ok(current_config) => {
+ let Some(backup_config_path) =
+ Config::find_free_backup_file_name(&config_file_path)
+ else {
+ log::error!("Failed to find a file name to back up the configuration to, ran out of files.");
+ return Err(None);
+ };
+ if Config::backup_config_with_written_content_confirmation(
+ &current_config,
+ &config_file_path,
+ &backup_config_path,
+ ) {
+ Ok(Some(backup_config_path))
+ } else {
+ log::error!(
+ "Failed to back up config file: {}",
+ backup_config_path.display()
+ );
+ Err(Some(backup_config_path))
+ }
+ },
+ Err(e) => {
+ if e.kind() == std::io::ErrorKind::NotFound {
+ Ok(None)
+ } else {
+ log::error!(
+ "Failed to read current config {}: {}",
+ config_file_path.display(),
+ e
+ );
+ Err(Some(config_file_path))
+ }
+ },
+ }
+ } else {
+ log::error!("No config file path found?");
+ Err(None)
+ }
+ }
+ fn autogen_config_message(backed_up_file_name: PathBuf) -> String {
+ format!("//\n// THIS FILE WAS AUTOGENERATED BY ZELLIJ, THE PREVIOUS FILE AT THIS LOCATION WAS COPIED TO: {}\n//\n\n", backed_up_file_name.display())
+ }
}
#[cfg(test)]
@@ -463,6 +622,7 @@ mod config_test {
white: PaletteColor::Rgb((255, 255, 255)),
..Default::default()
},
+ sourced_from_external_file: false,
},
);
let expected_themes = Themes::from_data(expected_themes);
@@ -520,6 +680,7 @@ mod config_test {
white: PaletteColor::Rgb((255, 255, 255)),
..Default::default()
},
+ sourced_from_external_file: false,
},
);
expected_themes.insert(
@@ -539,6 +700,7 @@ mod config_test {
orange: PaletteColor::Rgb((208, 135, 112)),
..Default::default()
},
+ sourced_from_external_file: false,
},
);
let expected_themes = Themes::from_data(expected_themes);
@@ -583,6 +745,7 @@ mod config_test {
white: PaletteColor::EightBit(255),
..Default::default()
},
+ sourced_from_external_file: false,
},
);
let expected_themes = Themes::from_data(expected_themes);
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 77411a5d..8278f638 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -504,6 +504,12 @@ impl PluginUserConfiguration {
configuration.remove("direction");
configuration.remove("floating");
configuration.remove("move_to_focused_tab");
+ configuration.remove("launch_new");
+ configuration.remove("payload");
+ configuration.remove("skip_cache");
+ configuration.remove("title");
+ configuration.remove("in_place");
+ configuration.remove("skip_plugin_cache");
PluginUserConfiguration(configuration)
}
@@ -735,6 +741,19 @@ pub struct FloatingPaneLayout {
}
impl FloatingPaneLayout {
+ pub fn new() -> Self {
+ FloatingPaneLayout {
+ name: None,
+ height: None,
+ width: None,
+ x: None,
+ y: None,
+ run: None,
+ focus: None,
+ already_running: false,
+ pane_initial_contents: None,
+ }
+ }
pub fn add_cwd_to_layout(&mut self, cwd: &PathBuf) {
match self.run.as_mut() {
Some(run) => run.add_cwd(cwd),
diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs
index bf0edf23..a7f212f0 100644
--- a/zellij-utils/src/input/theme.rs
+++ b/zellij-utils/src/input/theme.rs
@@ -37,7 +37,7 @@ impl FrameConfig {
}
}
-#[derive(Clone, PartialEq, Default)]
+#[derive(Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Themes(HashMap<String, Theme>);
impl fmt::Debug for Themes {
@@ -67,12 +67,16 @@ impl Themes {
pub fn get_theme(&self, theme_name: &str) -> Option<&Theme> {
self.0.get(theme_name)
}
+ pub fn inner(&self) -> &HashMap<String, Theme> {
+ &self.0
+ }
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Theme {
#[serde(flatten)]
pub palette: Palette,
+ pub sourced_from_external_file: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap
index 315c6d50..957ddf04 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap
@@ -1,5 +1,6 @@
---
source: zellij-utils/src/input/./unit/theme_test.rs
+assertion_line: 15
expression: "format!(\"{:#?}\", theme)"
---
{
@@ -103,5 +104,6 @@ expression: "format!(\"{:#?}\", theme)"
0,
),
},
+ sourced_from_external_file: true,
},
}
diff --git a/zellij-utils/src/ipc.rs b/zellij-utils/src/ipc.rs
index 2713ca78..85064f76 100644
--- a/zellij-utils/src/ipc.rs
+++ b/zellij-utils/src/ipc.rs
@@ -3,6 +3,7 @@ use crate::{
cli::CliArgs,
data::{ClientId, ConnectToSession, KeyWithModifier, Style},
errors::{get_current_ctx, prelude::*, ErrorContext},
+ input::config::Config,
input::keybinds::Keybinds,
input::{actions::Action, layout::Layout, options::Options, plugins::PluginAliases},
pane_size::{Size, SizeInPixels},
@@ -16,6 +17,7 @@ use std::{
io::{self, Write},
marker::PhantomData,
os::unix::io::{AsRawFd, FromRawFd},
+ path::PathBuf,
};
type SessionId = u64;
@@ -41,7 +43,6 @@ pub enum ClientType {
pub struct ClientAttributes {
pub size: Size,
pub style: Style,
- pub keybinds: Keybinds,
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
@@ -74,13 +75,16 @@ pub enum ClientToServerMsg {
NewClient(
ClientAttributes,
Box<CliArgs>,
- Box<Options>,
+ Box<Config>, // represents the saved configuration
+ Box<Options>, // represents the runtime configuration
Box<Layout>,
Box<PluginAliases>,
+ bool, // should launch setup wizard
),
AttachClient(
ClientAttributes,
- Options,
+ Config, // represents the saved configuration
+ Options, // represents the runtime configuration
Option<usize>, // tab position to focus
Option<(u32, bool)>, // (pane_id, is_plugin) => pane id to focus
),
@@ -90,6 +94,8 @@ pub enum ClientToServerMsg {
KillSession,
ConnStatus,
ListClients,
+ ConfigWrittenToDisk(Config),
+ FailedToWriteConfigToDisk(Option<PathBuf>),
}
// Types of messages sent from the server to the client
@@ -106,6 +112,7 @@ pub enum ServerToClientMsg {
UnblockCliPipeInput(String), // String -> pipe name
CliPipeOutput(String, String), // String -> pipe name, String -> Output
QueryTerminalSize,
+ WriteConfigToDisk { config: String },
}
#[derive(Serialize, Deserialize, Debug, Clone)]
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 37d02081..bfb01c78 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1,25 +1,27 @@
mod kdl_layout_parser;
use crate::data::{
- Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, LayoutInfo, Palette,
+ BareKey, Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, LayoutInfo, Palette,
PaletteColor, PaneInfo, PaneManifest, PermissionType, Resize, SessionInfo, TabInfo,
};
use crate::envs::EnvironmentVariables;
use crate::home::{find_default_config_dir, get_layout_dir};
use crate::input::config::{Config, ConfigError, KdlError};
use crate::input::keybinds::Keybinds;
-use crate::input::layout::{Layout, RunPlugin, RunPluginOrAlias};
+use crate::input::layout::{
+ Layout, PluginUserConfiguration, RunPlugin, RunPluginOrAlias, SplitSize,
+};
use crate::input::options::{Clipboard, OnForceClose, Options};
use crate::input::permission::{GrantedPermission, PermissionCache};
use crate::input::plugins::PluginAliases;
use crate::input::theme::{FrameConfig, Theme, Themes, UiConfig};
use kdl_layout_parser::KdlLayoutParser;
-use std::collections::{BTreeMap, HashMap, HashSet};
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use strum::IntoEnumIterator;
use uuid::Uuid;
use miette::NamedSource;
-use kdl::{KdlDocument, KdlEntry, KdlNode};
+use kdl::{KdlDocument, KdlEntry, KdlNode, KdlValue};
use std::path::PathBuf;
use std::str::FromStr;
@@ -548,6 +550,526 @@ impl Action {
)),
}
}
+ pub fn to_kdl(&self) -> Option<KdlNode> {
+ match self {
+ Action::Quit => Some(KdlNode::new("Quit")),
+ Action::Write(_key, bytes, _is_kitty) => {
+ let mut node = KdlNode::new("Write");
+ for byte in bytes {
+ node.push(KdlValue::Base10(*byte as i64));
+ }
+ Some(node)
+ },
+ Action::WriteChars(string) => {
+ let mut node = KdlNode::new("WriteChars");
+ node.push(string.clone());
+ Some(node)
+ },
+ Action::SwitchToMode(input_mode) => {
+ let mut node = KdlNode::new("SwitchToMode");
+ node.push(format!("{:?}", input_mode).to_lowercase());
+ Some(node)
+ },
+ Action::Resize(resize, resize_direction) => {
+ let mut node = KdlNode::new("Resize");
+ let resize = match resize {
+ Resize::Increase => "Increase",
+ Resize::Decrease => "Decrease",
+ };
+ if let Some(resize_direction) = resize_direction {
+ let resize_direction = match resize_direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ node.push(format!("{} {}", resize, resize_direction));
+ } else {
+ node.push(format!("{}", resize));
+ }
+ Some(node)
+ },
+ Action::FocusNextPane => Some(KdlNode::new("FocusNextPane")),
+ Action::FocusPreviousPane => Some(KdlNode::new("FocusPreviousPane")),
+ Action::SwitchFocus => Some(KdlNode::new("SwitchFocus")),
+ Action::MoveFocus(direction) => {
+ let mut node = KdlNode::new("MoveFocus");
+ let direction = match direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ node.push(direction);
+ Some(node)
+ },
+ Action::MoveFocusOrTab(direction) => {
+ let mut node = KdlNode::new("MoveFocusOrTab");
+ let direction = match direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ node.push(direction);
+ Some(node)
+ },
+ Action::MovePane(direction) => {
+ let mut node = KdlNode::new("MovePane");
+ if let Some(direction) = direction {
+ let direction = match direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ node.push(direction);
+ }
+ Some(node)
+ },
+ Action::MovePaneBackwards => Some(KdlNode::new("MovePaneBackwards")),
+ Action::DumpScreen(file, _) => {
+ let mut node = KdlNode::new("DumpScreen");
+ node.push(file.clone());
+ Some(node)
+ },
+ Action::DumpLayout => Some(KdlNode::new("DumpLayout")),
+ Action::EditScrollback => Some(KdlNode::new("EditScrollback")),
+ Action::ScrollUp => Some(KdlNode::new("ScrollUp")),
+ Action::ScrollDown => Some(KdlNode::new("ScrollDown")),
+ Action::ScrollToBottom => Some(KdlNode::new("ScrollToBottom")),
+ Action::ScrollToTop => Some(KdlNode::new("ScrollToTop")),
+ Action::PageScrollUp => Some(KdlNode::new("PageScrollUp")),
+ Action::PageScrollDown => Some(KdlNode::new("PageScrollDown")),
+ Action::HalfPageScrollUp => Some(KdlNode::new("HalfPageScrollUp")),
+ Action::HalfPageScrollDown => Some(KdlNode::new("HalfPageScrollDown")),
+ Action::ToggleFocusFullscreen => Some(KdlNode::new("ToggleFocusFullscreen")),
+ Action::TogglePaneFrames => Some(KdlNode::new("TogglePaneFrames")),
+ Action::ToggleActiveSyncTab => Some(KdlNode::new("ToggleActiveSyncTab")),
+ Action::NewPane(direction, _, _) => {
+ let mut node = KdlNode::new("NewPane");
+ if let Some(direction) = direction {
+ let direction = match direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ node.push(direction);
+ }
+ Some(node)
+ },
+ Action::TogglePaneEmbedOrFloating => Some(KdlNode::new("TogglePaneEmbedOrFloating")),
+ Action::ToggleFloatingPanes => Some(KdlNode::new("ToggleFloatingPanes")),
+ Action::CloseFocus => Some(KdlNode::new("CloseFocus")),
+ Action::PaneNameInput(bytes) => {
+ let mut node = KdlNode::new("PaneNameInput");
+ for byte in bytes {
+ node.push(KdlValue::Base10(*byte as i64));
+ }
+ Some(node)
+ },
+ Action::UndoRenamePane => Some(KdlNode::new("UndoRenamePane")),
+ Action::NewTab(_, _, _, _, name) => {
+ log::warn!("Converting new tab action without arguments, original action saved to .bak.kdl file");
+ let mut node = KdlNode::new("NewTab");
+ if let Some(name) = name {
+ let mut children = KdlDocument::new();
+ let mut name_node = KdlNode::new("name");
+ name_node.push(name.clone());
+ children.nodes_mut().push(name_node);
+ node.set_children(children);
+ }
+ Some(node)
+ },
+ Action::GoToNextTab => Some(KdlNode::new("GoToNextTab")),
+ Action::GoToPreviousTab => Some(KdlNode::new("GoToPreviousTab")),
+ Action::CloseTab => Some(KdlNode::new("CloseTab")),
+ Action::GoToTab(index) => {
+ let mut node = KdlNode::new("GoToTab");
+ node.push(KdlValue::Base10(*index as i64));
+ Some(node)
+ },
+ Action::ToggleTab => Some(KdlNode::new("ToggleTab")),
+ Action::TabNameInput(bytes) => {
+ let mut node = KdlNode::new("TabNameInput");
+ for byte in bytes {
+ node.push(KdlValue::Base10(*byte as i64));
+ }
+ Some(node)
+ },
+ Action::UndoRenameTab => Some(KdlNode::new("UndoRenameTab")),
+ Action::MoveTab(direction) => {
+ let mut node = KdlNode::new("MoveTab");
+ let direction = match direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ node.push(direction);
+ Some(node)
+ },
+ Action::NewTiledPane(direction, run_command_action, name) => {
+ let mut node = KdlNode::new("Run");
+ let mut node_children = KdlDocument::new();
+ if let Some(run_command_action) = run_command_action {
+ node.push(run_command_action.command.display().to_string());
+ for arg in &run_command_action.args {
+ node.push(arg.clone());
+ }
+ if let Some(cwd) = &run_command_action.cwd {
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ node_children.nodes_mut().push(cwd_node);
+ }
+ if run_command_action.hold_on_start {
+ let mut hos_node = KdlNode::new("hold_on_start");
+ hos_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(hos_node);
+ }
+ if !run_command_action.hold_on_close {
+ let mut hoc_node = KdlNode::new("hold_on_close");
+ hoc_node.push(KdlValue::Bool(false));
+ node_children.nodes_mut().push(hoc_node);
+ }
+ }
+ if let Some(name) = name {
+ let mut name_node = KdlNode::new("name");
+ name_node.push(name.clone());
+ node_children.nodes_mut().push(name_node);
+ }
+ if let Some(direction) = direction {
+ let mut direction_node = KdlNode::new("direction");
+ let direction = match direction {
+ Direction::Left => "left",
+ Direction::Right => "right",
+ Direction::Up => "up",
+ Direction::Down => "down",
+ };
+ direction_node.push(direction);
+ node_children.nodes_mut().push(direction_node);
+ }
+ if !node_children.nodes().is_empty() {
+ node.set_children(node_children);
+ }
+ Some(node)
+ },
+ Action::NewFloatingPane(run_command_action, name, floating_pane_coordinates) => {
+ let mut node = KdlNode::new("Run");
+ let mut node_children = KdlDocument::new();
+ let mut floating_pane = KdlNode::new("floating");
+ floating_pane.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(floating_pane);
+ if let Some(run_command_action) = run_command_action {
+ node.push(run_command_action.command.display().to_string());
+ for arg in &run_command_action.args {
+ node.push(arg.clone());
+ }
+ if let Some(cwd) = &run_command_action.cwd {
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ node_children.nodes_mut().push(cwd_node);
+ }
+ if run_command_action.hold_on_start {
+ let mut hos_node = KdlNode::new("hold_on_start");
+ hos_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(hos_node);
+ }
+ if !run_command_action.hold_on_close {
+ let mut hoc_node = KdlNode::new("hold_on_close");
+ hoc_node.push(KdlValue::Bool(false));
+ node_children.nodes_mut().push(hoc_node);
+ }
+ }
+ if let Some(floating_pane_coordinates) = floating_pane_coordinates {
+ if let Some(x) = floating_pane_coordinates.x {
+ let mut x_node = KdlNode::new("x");
+ match x {
+ SplitSize::Percent(x) => {
+ x_node.push(format!("{}%", x));
+ },
+ SplitSize::Fixed(x) => {
+ x_node.push(KdlValue::Base10(x as i64));
+ },
+ };
+ node_children.nodes_mut().push(x_node);
+ }
+ if let Some(y) = floating_pane_coordinates.y {
+ let mut y_node = KdlNode::new("y");
+ match y {
+ SplitSize::Percent(y) => {
+ y_node.push(format!("{}%", y));
+ },
+ SplitSize::Fixed(y) => {
+ y_node.push(KdlValue::Base10(y as i64));
+ },
+ };
+ node_children.nodes_mut().push(y_node);
+ }
+ if let Some(width) = floating_pane_coordinates.width {
+ let mut width_node = KdlNode::new("width");
+ match width {
+ SplitSize::Percent(width) => {
+ width_node.push(format!("{}%", width));
+ },
+ SplitSize::Fixed(width) => {
+ width_node.push(KdlValue::Base10(width as i64));
+ },
+ };
+ node_children.nodes_mut().push(width_node);
+ }
+ if let Some(height) = floating_pane_coordinates.height {
+ let mut height_node = KdlNode::new("height");
+ match height {
+ SplitSize::Percent(height) => {
+ height_node.push(format!("{}%", height));
+ },
+ SplitSize::Fixed(height) => {
+ height_node.push(KdlValue::Base10(height as i64));
+ },
+ };
+ node_children.nodes_mut().push(height_node);
+ }
+ }
+ if let Some(name) = name {
+ let mut name_node = KdlNode::new("name");
+ name_node.push(name.clone());
+ node_children.nodes_mut().push(name_node);
+ }
+ if !node_children.nodes().is_empty() {
+ node.set_children(node_children);
+ }
+ Some(node)
+ },
+ Action::NewInPlacePane(run_command_action, name) => {
+ let mut node = KdlNode::new("Run");
+ let mut node_children = KdlDocument::new();
+ if let Some(run_command_action) = run_command_action {
+ node.push(run_command_action.command.display().to_string());
+ for arg in &run_command_action.args {
+ node.push(arg.clone());
+ }
+ let mut in_place_node = KdlNode::new("in_place");
+ in_place_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(in_place_node);
+ if let Some(cwd) = &run_command_action.cwd {
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ node_children.nodes_mut().push(cwd_node);
+ }
+ if run_command_action.hold_on_start {
+ let mut hos_node = KdlNode::new("hold_on_start");
+ hos_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(hos_node);
+ }
+ if !run_command_action.hold_on_close {
+ let mut hoc_node = KdlNode::new("hold_on_close");
+ hoc_node.push(KdlValue::Bool(false));
+ node_children.nodes_mut().push(hoc_node);
+ }
+ }
+ if let Some(name) = name {
+ let mut name_node = KdlNode::new("name");
+ name_node.push(name.clone());
+ node_children.nodes_mut().push(name_node);
+ }
+ if !node_children.nodes().is_empty() {
+ node.set_children(node_children);
+ }
+ Some(node)
+ },
+ Action::Detach => Some(KdlNode::new("Detach")),
+ Action::LaunchOrFocusPlugin(
+ run_plugin_or_alias,
+ should_float,
+ move_to_focused_tab,
+ should_open_in_place,
+ skip_plugin_cache,
+ ) => {
+ let mut node = KdlNode::new("LaunchOrFocusPlugin");
+ let mut node_children = KdlDocument::new();
+ let location = run_plugin_or_alias.location_string();
+ node.push(location);
+ if *should_float {
+ let mut should_float_node = KdlNode::new("floating");
+ should_float_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(should_float_node);
+ }
+ if *move_to_focused_tab {
+ let mut move_to_focused_tab_node = KdlNode::new("move_to_focused_tab");
+ move_to_focused_tab_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(move_to_focused_tab_node);
+ }
+ if *should_open_in_place {
+ let mut should_open_in_place_node = KdlNode::new("in_place");
+ should_open_in_place_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(should_open_in_place_node);
+ }
+ if *skip_plugin_cache {
+ let mut skip_plugin_cache_node = KdlNode::new("skip_plugin_cache");
+ skip_plugin_cache_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(skip_plugin_cache_node);
+ }
+ if let Some(configuration) = run_plugin_or_alias.get_configuration() {
+ for (config_key, config_value) in configuration.inner().iter() {
+ let mut node = KdlNode::new(config_key.clone());
+ node.push(config_value.clone());
+ node_children.nodes_mut().push(node);
+ }
+ }
+ if !node_children.nodes().is_empty() {
+ node.set_children(node_children);
+ }
+ Some(node)
+ },
+ Action::LaunchPlugin(
+ run_plugin_or_alias,
+ should_float,
+ should_open_in_place,
+ skip_plugin_cache,
+ cwd,
+ ) => {
+ let mut node = KdlNode::new("LaunchPlugin");
+ let mut node_children = KdlDocument::new();
+ let location = run_plugin_or_alias.location_string();
+ node.push(location);
+ if *should_float {
+ let mut should_float_node = KdlNode::new("floating");
+ should_float_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(should_float_node);
+ }
+ if *should_open_in_place {
+ let mut should_open_in_place_node = KdlNode::new("in_place");
+ should_open_in_place_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(should_open_in_place_node);
+ }
+ if *skip_plugin_cache {
+ let mut skip_plugin_cache_node = KdlNode::new("skip_plugin_cache");
+ skip_plugin_cache_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(skip_plugin_cache_node);
+ }
+ if let Some(cwd) = &cwd {
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ node_children.nodes_mut().push(cwd_node);
+ } else if let Some(cwd) = run_plugin_or_alias.get_initial_cwd() {
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ node_children.nodes_mut().push(cwd_node);
+ }
+ if let Some(configuration) = run_plugin_or_alias.get_configuration() {
+ for (config_key, config_value) in configuration.inner().iter() {
+ let mut node = KdlNode::new(config_key.clone());
+ node.push(config_value.clone());
+ node_children.nodes_mut().push(node);
+ }
+ }
+ if !node_children.nodes().is_empty() {
+ node.set_children(node_children);
+ }
+ Some(node)
+ },
+ Action::Copy => Some(KdlNode::new("Copy")),
+ Action::SearchInput(bytes) => {
+ let mut node = KdlNode::new("SearchInput");
+ for byte in bytes {
+ node.push(KdlValue::Base10(*byte as i64));
+ }
+ Some(node)
+ },
+ Action::Search(search_direction) => {
+ let mut node = KdlNode::new("Search");
+ let direction = match search_direction {
+ SearchDirection::Down => "down",
+ SearchDirection::Up => "up",
+ };
+ node.push(direction);
+ Some(node)
+ },
+ Action::SearchToggleOption(search_toggle_option) => {
+ let mut node = KdlNode::new("SearchToggleOption");
+ node.push(format!("{:?}", search_toggle_option));
+ Some(node)
+ },
+ Action::ToggleMouseMode => Some(KdlNode::new("ToggleMouseMode")),
+ Action::PreviousSwapLayout => Some(KdlNode::new("PreviousSwapLayout")),
+ Action::NextSwapLayout => Some(KdlNode::new("NextSwapLayout")),
+ Action::BreakPane => Some(KdlNode::new("BreakPane")),
+ Action::BreakPaneRight => Some(KdlNode::new("BreakPaneRight")),
+ Action::BreakPaneLeft => Some(KdlNode::new("BreakPaneLeft")),
+ Action::KeybindPipe {
+ name,
+ payload,
+ args,
+ plugin,
+ configuration,
+ launch_new,
+ skip_cache,
+ floating,
+ in_place,
+ cwd,
+ pane_title,
+ } => {
+ let mut node = KdlNode::new("MessagePlugin");
+ let mut node_children = KdlDocument::new();
+ if let Some(plugin) = plugin {
+ node.push(plugin.clone());
+ }
+ if let Some(name) = name {
+ let mut name_node = KdlNode::new("name");
+ name_node.push(name.clone());
+ node_children.nodes_mut().push(name_node);
+ }
+ if let Some(cwd) = cwd {
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ node_children.nodes_mut().push(cwd_node);
+ }
+ if let Some(payload) = payload {
+ let mut payload_node = KdlNode::new("payload");
+ payload_node.push(payload.clone());
+ node_children.nodes_mut().push(payload_node);
+ }
+ if *launch_new {
+ let mut launch_new_node = KdlNode::new("launch_new");
+ launch_new_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(launch_new_node);
+ }
+ if *skip_cache {
+ let mut skip_cache_node = KdlNode::new("skip_cache");
+ skip_cache_node.push(KdlValue::Bool(true));
+ node_children.nodes_mut().push(skip_cache_node);
+ }
+ if let Some(floating) = floating {
+ let mut floating_node = KdlNode::new("floating");
+ floating_node.push(KdlValue::Bool(*floating));
+ node_children.nodes_mut().push(floating_node);
+ }
+ if let Some(title) = pane_title {
+ let mut title_node = KdlNode::new("title");
+ title_node.push(title.clone());
+ node_children.nodes_mut().push(title_node);
+ }
+ if let Some(configuration) = configuration {
+ // we do this because the constructor removes the relevant config fields from
+ // above, otherwise we would have duplicates
+ let configuration = PluginUserConfiguration::new(configuration.clone());
+ let configuration = configuration.inner();
+ for (config_key, config_value) in configuration.iter() {
+ let mut node = KdlNode::new(config_key.clone());
+ node.push(config_value.clone());
+ node_children.nodes_mut().push(node);
+ }
+ }
+ if !node_children.nodes().is_empty() {
+ node.set_children(node_children);
+ }
+ Some(node)
+ },
+ _ => None,
+ }
+ }
}
impl TryFrom<(&str, &KdlDocument)> for PaletteColor {
@@ -667,6 +1189,23 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor {
}
}
+impl PaletteColor {
+ pub fn to_kdl(&self, color_name: &str) -> KdlNode {
+ let mut node = KdlNode::new(color_name);
+ match self {
+ PaletteColor::Rgb((r, g, b)) => {
+ node.push(KdlValue::Base10(*r as i64));
+ node.push(KdlValue::Base10(*g as i64));
+ node.push(KdlValue::Base10(*b as i64));
+ },
+ PaletteColor::EightBit(color_index) => {
+ node.push(KdlValue::Base10(*color_index as i64));
+ },
+ }
+ node
+ }
+}
+
impl TryFrom<(&KdlNode, &Options)> for Action {
type Error = ConfigError;
fn try_from((kdl_action, config_options): (&KdlNode, &Options)) -> Result<Self, Self::Error> {
@@ -1654,6 +2193,876 @@ impl Options {
let document: KdlDocument = stringified_keybindings.parse()?;
Options::from_kdl(&document)
}
+ fn simplified_ui_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Use a simplified UI without special fonts (arrow glyphs)",
+ "// Options:",
+ "// - true",
+ "// - false (Default)",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("simplified_ui");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(simplified_ui) = self.simplified_ui {
+ let mut node = create_node(simplified_ui);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(true);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn theme_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}",
+ " ",
+ "// Choose the theme that is specified in the themes section.",
+ "// Default: default",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("theme");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(theme) = &self.theme {
+ let mut node = create_node(theme);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("dracula");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn default_mode_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}",
+ " ", "// Choose the base input mode of zellij.", "// Default: normal", "// "
+ );
+
+ let create_node = |default_mode: &InputMode| -> KdlNode {
+ let mut node = KdlNode::new("default_mode");
+ node.push(format!("{:?}", default_mode).to_lowercase());
+ node
+ };
+ if let Some(default_mode) = &self.default_mode {
+ let mut node = create_node(default_mode);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(&InputMode::Locked);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn default_shell_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text =
+ format!("{}\n{}\n{}\n{}",
+ " ",
+ "// Choose the path to the default shell that zellij will use for opening new panes",
+ "// Default: $SHELL",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("default_shell");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(default_shell) = &self.default_shell {
+ let mut node = create_node(&default_shell.display().to_string());
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("fish");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn default_cwd_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}",
+ " ",
+ "// Choose the path to override cwd that zellij will use for opening new panes",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("default_cwd");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(default_cwd) = &self.default_cwd {
+ let mut node = create_node(&default_cwd.display().to_string());
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("/tmp");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn default_layout_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}",
+ " ",
+ "// The name of the default layout to load on startup",
+ "// Default: \"default\"",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("default_layout");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(default_layout) = &self.default_layout {
+ let mut node = create_node(&default_layout.display().to_string());
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("compact");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn layout_dir_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}",
+ " ", "// The folder in which Zellij will look for layouts", "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("layout_dir");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(layout_dir) = &self.layout_dir {
+ let mut node = create_node(&layout_dir.display().to_string());
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("/tmp");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn theme_dir_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}",
+ " ", "// The folder in which Zellij will look for themes", "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("theme_dir");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(theme_dir) = &self.theme_dir {
+ let mut node = create_node(&theme_dir.display().to_string());
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("/tmp");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn mouse_mode_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Toggle enabling the mouse mode.",
+ "// On certain configurations, or terminals this could",
+ "// potentially interfere with copying text.",
+ "// Options:",
+ "// - true (default)",
+ "// - false",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("mouse_mode");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(mouse_mode) = self.mouse_mode {
+ let mut node = create_node(mouse_mode);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn pane_frames_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Toggle having pane frames around the panes",
+ "// Options:",
+ "// - true (default, enabled)",
+ "// - false",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("pane_frames");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(pane_frames) = self.pane_frames {
+ let mut node = create_node(pane_frames);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn mirror_session_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// 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",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("mirror_session");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(mirror_session) = self.mirror_session {
+ let mut node = create_node(mirror_session);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(true);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn on_force_close_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// 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",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("on_force_close");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(on_force_close) = &self.on_force_close {
+ let mut node = match on_force_close {
+ OnForceClose::Detach => create_node("detach"),
+ OnForceClose::Quit => create_node("quit"),
+ };
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("quit");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn scroll_buffer_size_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// 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",
+ "// ",
+ );
+
+ let create_node = |node_value: usize| -> KdlNode {
+ let mut node = KdlNode::new("scroll_buffer_size");
+ node.push(KdlValue::Base10(node_value as i64));
+ node
+ };
+ if let Some(scroll_buffer_size) = self.scroll_buffer_size {
+ let mut node = create_node(scroll_buffer_size);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(10000);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn copy_command_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// 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",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("copy_command");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(copy_command) = &self.copy_command {
+ let mut node = create_node(copy_command);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("pbcopy");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn copy_clipboard_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!("{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// 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",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("copy_clipboard");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(copy_clipboard) = &self.copy_clipboard {
+ let mut node = match copy_clipboard {
+ Clipboard::Primary => create_node("primary"),
+ Clipboard::System => create_node("system"),
+ };
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("primary");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn copy_on_select_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}",
+ " ",
+ "// Enable automatic copying (and clearing) of selection when releasing mouse",
+ "// Default: true",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("copy_on_select");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(copy_on_select) = self.copy_on_select {
+ let mut node = create_node(copy_on_select);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(true);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn scrollback_editor_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}",
+ " ",
+ "// Path to the default editor to use to edit pane scrollbuffer",
+ "// Default: $EDITOR or $VISUAL",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("scrollback_editor");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(scrollback_editor) = &self.scrollback_editor {
+ let mut node = create_node(&scrollback_editor.display().to_string());
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("/usr/bin/vim");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn session_name_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// A fixed name to always give the Zellij session.",
+ "// Consider also setting `attach_to_session true,`",
+ "// otherwise this will error if such a session exists.",
+ "// Default: <RANDOM>",
+ "// ",
+ );
+
+ let create_node = |node_value: &str| -> KdlNode {
+ let mut node = KdlNode::new("session_name");
+ node.push(node_value.to_owned());
+ node
+ };
+ if let Some(session_name) = &self.session_name {
+ let mut node = create_node(&session_name);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node("My singleton session");
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn attach_to_session_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// When `session_name` is provided, attaches to that session",
+ "// if it is already running or creates it otherwise.",
+ "// Default: false",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("attach_to_session");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(attach_to_session) = self.attach_to_session {
+ let mut node = create_node(attach_to_session);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(true);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn auto_layout_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!("{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible",
+ "// Options:",
+ "// - true (default)",
+ "// - false",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("auto_layout");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(auto_layout) = self.auto_layout {
+ let mut node = create_node(auto_layout);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn session_serialization_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!("{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Whether sessions should be serialized to the cache folder (including their tabs/panes, cwds and running commands) so that they can later be resurrected",
+ "// Options:",
+ "// - true (default)",
+ "// - false",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("session_serialization");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(session_serialization) = self.session_serialization {
+ let mut node = create_node(session_serialization);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn serialize_pane_viewport_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Whether pane viewports are serialized along with the session, default is false",
+ "// Options:",
+ "// - true",
+ "// - false (default)",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("serialize_pane_viewport");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(serialize_pane_viewport) = self.serialize_pane_viewport {
+ let mut node = create_node(serialize_pane_viewport);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn scrollback_lines_to_serialize_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!("{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// 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. This does nothing if `serialize_pane_viewport` is not true.",
+ "// ",
+ );
+
+ let create_node = |node_value: usize| -> KdlNode {
+ let mut node = KdlNode::new("scrollback_lines_to_serialize");
+ node.push(KdlValue::Base10(node_value as i64));
+ node
+ };
+ if let Some(scrollback_lines_to_serialize) = self.scrollback_lines_to_serialize {
+ let mut node = create_node(scrollback_lines_to_serialize);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(10000);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn styled_underlines_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Enable or disable the rendering of styled and colored underlines (undercurl).",
+ "// May need to be disabled for certain unsupported terminals",
+ "// Default: true",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("styled_underlines");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(styled_underlines) = self.styled_underlines {
+ let mut node = create_node(styled_underlines);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn serialization_interval_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!(
+ "{}\n{}\n{}",
+ " ", "// How often in seconds sessions are serialized", "// ",
+ );
+
+ let create_node = |node_value: u64| -> KdlNode {
+ let mut node = KdlNode::new("serialization_interval");
+ node.push(KdlValue::Base10(node_value as i64));
+ node
+ };
+ if let Some(serialization_interval) = self.serialization_interval {
+ let mut node = create_node(serialization_interval);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(10000);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn disable_session_metadata_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!("{}\n{}\n{}\n{}\n{}",
+ " ",
+ "// Enable or disable writing of session metadata to disk (if disabled, other sessions might not know",
+ "// metadata info on this session)",
+ "// Default: false",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("disable_session_metadata");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(disable_session_metadata) = self.disable_session_metadata {
+ let mut node = create_node(disable_session_metadata);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ fn support_kitty_keyboard_protocol_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
+ let comment_text = format!("{}\n{}\n{}\n{}",
+ " ",
+ "// Enable or disable support for the enhanced Kitty Keyboard Protocol (the host terminal must also support it)",
+ "// Default: true (if the host terminal supports it)",
+ "// ",
+ );
+
+ let create_node = |node_value: bool| -> KdlNode {
+ let mut node = KdlNode::new("support_kitty_keyboard_protocol");
+ node.push(KdlValue::Bool(node_value));
+ node
+ };
+ if let Some(support_kitty_keyboard_protocol) = self.support_kitty_keyboard_protocol {
+ let mut node = create_node(support_kitty_keyboard_protocol);
+ if add_comments {
+ node.set_leading(format!("{}\n", comment_text));
+ }
+ Some(node)
+ } else if add_comments {
+ let mut node = create_node(false);
+ node.set_leading(format!("{}\n// ", comment_text));
+ Some(node)
+ } else {
+ None
+ }
+ }
+ pub fn to_kdl(&self, add_comments: bool) -> Vec<KdlNode> {
+ let mut nodes = vec![];
+ if let Some(simplified_ui_node) = self.simplified_ui_to_kdl(add_comments) {
+ nodes.push(simplified_ui_node);
+ }
+ if let Some(theme_node) = self.theme_to_kdl(add_comments) {
+ nodes.push(theme_node);
+ }
+ if let Some(default_mode) = self.default_mode_to_kdl(add_comments) {
+ nodes.push(default_mode);
+ }
+ if let Some(default_shell) = self.default_shell_to_kdl(add_comments) {
+ nodes.push(default_shell);
+ }
+ if let Some(default_cwd) = self.default_cwd_to_kdl(add_comments) {
+ nodes.push(default_cwd);
+ }
+ if let Some(default_layout) = self.default_layout_to_kdl(add_comments) {
+ nodes.push(default_layout);
+ }
+ if let Some(layout_dir) = self.layout_dir_to_kdl(add_comments) {
+ nodes.push(layout_dir);
+ }
+ if let Some(theme_dir) = self.theme_dir_to_kdl(add_comments) {
+ nodes.push(theme_dir);
+ }
+ if let Some(mouse_mode) = self.mouse_mode_to_kdl(add_comments) {
+ nodes.push(mouse_mode);
+ }
+ if let Some(pane_frames) = self.pane_frames_to_kdl(add_comments) {
+ nodes.push(pane_frames);
+ }
+ if let Some(mirror_session) = self.mirror_session_to_kdl(add_comments) {
+ nodes.push(mirror_session);
+ }
+ if let Some(on_force_close) = self.on_force_close_to_kdl(add_comments) {
+ nodes.push(on_force_close);
+ }
+ if let Some(scroll_buffer_size) = self.scroll_buffer_size_to_kdl(add_comments) {
+ nodes.push(scroll_buffer_size);
+ }
+ if let Some(copy_command) = self.copy_command_to_kdl(add_comments) {
+ nodes.push(copy_command);
+ }
+ if let Some(copy_clipboard) = self.copy_clipboard_to_kdl(add_comments) {
+ nodes.push(copy_clipboard);
+ }
+ if let Some(copy_on_select) = self.copy_on_select_to_kdl(add_comments) {
+ nodes.push(copy_on_select);
+ }
+ if let Some(scrollback_editor) = self.scrollback_editor_to_kdl(add_comments) {
+ nodes.push(scrollback_editor);
+ }
+ if let Some(session_name) = self.session_name_to_kdl(add_comments) {
+ nodes.push(session_name);
+ }
+ if let Some(attach_to_session) = self.attach_to_session_to_kdl(add_comments) {
+ nodes.push(attach_to_session);
+ }
+ if let Some(auto_layout) = self.auto_layout_to_kdl(add_comments) {
+ nodes.push(auto_layout);
+ }
+ if let Some(session_serialization) = self.session_serialization_to_kdl(add_comments) {
+ nodes.push(session_serialization);
+ }
+ if let Some(serialize_pane_viewport) = self.serialize_pane_viewport_to_kdl(add_comments) {
+ nodes.push(serialize_pane_viewport);
+ }
+ if let Some(scrollback_lines_to_serialize) =
+ self.scrollback_lines_to_serialize_to_kdl(add_comments)
+ {
+ nodes.push(scrollback_lines_to_serialize);
+ }
+ if let Some(styled_underlines) = self.styled_underlines_to_kdl(add_comments) {
+ nodes.push(styled_underlines);
+ }
+ if let Some(serialization_interval) = self.serialization_interval_to_kdl(add_comments) {
+ nodes.push(serialization_interval);
+ }
+ if let Some(disable_session_metadata) = self.disable_session_metadata_to_kdl(add_comments) {
+ nodes.push(disable_session_metadata);
+ }
+ if let Some(support_kitty_keyboard_protocol) =
+ self.support_kitty_keyboard_protocol_to_kdl(add_comments)
+ {
+ nodes.push(support_kitty_keyboard_protocol);
+ }
+ nodes
+ }
}
impl Layout {
@@ -1744,6 +3153,29 @@ impl EnvironmentVariables {
}
Ok(EnvironmentVariables::from_data(env))
}
+ pub fn to_kdl(&self) -> Option<KdlNode> {
+ let mut has_env_vars = false;
+ let mut env = KdlNode::new("env");
+ let mut env_vars = KdlDocument::new();
+
+ let mut stable_sorted = BTreeMap::new();
+ for (env_var_name, env_var_value) in self.inner() {
+ stable_sorted.insert(env_var_name, env_var_value);
+ }
+ for (env_key, env_value) in stable_sorted {
+ has_env_vars = true;
+ let mut variable_key = KdlNode::new(env_key.to_owned());
+ variable_key.push(env_value.to_owned());
+ env_vars.nodes_mut().push(variable_key);
+ }
+
+ if has_env_vars {
+ env.set_children(env_vars);
+ Some(env)
+ } else {
+ None
+ }
+ }
}
impl Keybinds {
@@ -1904,6 +3336,184 @@ impl Keybinds {
))
}
}
+ // minimize keybind entries for serialization, so that duplicate entries will appear in
+ // "shared" nodes later rather than once per mode
+ fn minimize_entries(
+ &self,
+ ) -> BTreeMap<BTreeSet<InputMode>, BTreeMap<KeyWithModifier, Vec<Action>>> {
+ let mut minimized: BTreeMap<BTreeSet<InputMode>, BTreeMap<KeyWithModifier, Vec<Action>>> =
+ BTreeMap::new();
+ let mut flattened: Vec<BTreeMap<KeyWithModifier, Vec<Action>>> = self
+ .0
+ .iter()
+ .map(|(_input_mode, keybind)| keybind.clone().into_iter().collect())
+ .collect();
+ for keybind in flattened.drain(..) {
+ for (key, actions) in keybind.into_iter() {
+ let mut appears_in_modes: BTreeSet<InputMode> = BTreeSet::new();
+ for (input_mode, keybinds) in self.0.iter() {
+ if keybinds.get(&key) == Some(&actions) {
+ appears_in_modes.insert(*input_mode);
+ }
+ }
+ minimized
+ .entry(appears_in_modes)
+ .or_insert_with(Default::default)
+ .insert(key, actions);
+ }
+ }
+ minimized
+ }
+ fn serialize_mode_title_node(&self, input_modes: &BTreeSet<InputMode>) -> KdlNode {
+ let all_modes: Vec<InputMode> = InputMode::iter().collect();
+ let total_input_mode_count = all_modes.len();
+ if input_modes.len() == 1 {
+ let input_mode_name =
+ format!("{:?}", input_modes.iter().next().unwrap()).to_lowercase();
+ KdlNode::new(input_mode_name)
+ } else if input_modes.len() == total_input_mode_count {
+ KdlNode::new("shared")
+ } else if input_modes.len() < total_input_mode_count / 2 {
+ let mut node = KdlNode::new("shared_among");
+ for input_mode in input_modes {
+ node.push(format!("{:?}", input_mode).to_lowercase());
+ }
+ node
+ } else {
+ let mut node = KdlNode::new("shared_except");
+ let mut modes = all_modes.clone();
+ for input_mode in input_modes {
+ modes.retain(|m| m != input_mode)
+ }
+ for mode in modes {
+ node.push(format!("{:?}", mode).to_lowercase());
+ }
+ node
+ }
+ }
+ fn serialize_mode_keybinds(
+ &self,
+ keybinds: &BTreeMap<KeyWithModifier, Vec<Action>>,
+ ) -> KdlDocument {
+ let mut mode_keybinds = KdlDocument::new();
+ for keybind in keybinds {
+ let mut keybind_node = KdlNode::new("bind");
+ keybind_node.push(keybind.0.to_kdl());
+ let mut actions = KdlDocument::new();
+ let mut actions_have_children = false;
+ for action in keybind.1 {
+ if let Some(kdl_action) = action.to_kdl() {
+ if kdl_action.children().is_some() {
+ actions_have_children = true;
+ }
+ actions.nodes_mut().push(kdl_action);
+ }
+ }
+ if !actions_have_children {
+ for action in actions.nodes_mut() {
+ action.set_leading("");
+ action.set_trailing("; ");
+ }
+ actions.set_leading(" ");
+ actions.set_trailing("");
+ }
+ keybind_node.set_children(actions);
+ mode_keybinds.nodes_mut().push(keybind_node);
+ }
+ mode_keybinds
+ }
+ pub fn to_kdl(&self, should_clear_defaults: bool) -> KdlNode {
+ let mut keybinds_node = KdlNode::new("keybinds");
+ if should_clear_defaults {
+ keybinds_node.insert("clear-defaults", true);
+ }
+ let mut minimized = self.minimize_entries();
+ let mut keybinds_children = KdlDocument::new();
+
+ macro_rules! encode_single_input_mode {
+ ($mode_name:ident) => {{
+ if let Some(keybinds) = minimized.remove(&BTreeSet::from([InputMode::$mode_name])) {
+ let mut mode_node =
+ KdlNode::new(format!("{:?}", InputMode::$mode_name).to_lowercase());
+ let mode_keybinds = self.serialize_mode_keybinds(&keybinds);
+ mode_node.set_children(mode_keybinds);
+ keybinds_children.nodes_mut().push(mode_node);
+ }
+ }};
+ }
+ // we do this explicitly so that the sorting order of modes in the config is more Human
+ // readable - this is actually less code (and clearer) than implementing Ord in this case
+ encode_single_input_mode!(Normal);
+ encode_single_input_mode!(Locked);
+ encode_single_input_mode!(Pane);
+ encode_single_input_mode!(Tab);
+ encode_single_input_mode!(Resize);
+ encode_single_input_mode!(Move);
+ encode_single_input_mode!(Scroll);
+ encode_single_input_mode!(Search);
+ encode_single_input_mode!(Session);
+
+ for (input_modes, keybinds) in minimized {
+ if input_modes.is_empty() {
+ log::error!("invalid input mode for keybinds: {:#?}", keybinds);
+ continue;
+ }
+ let mut mode_node = self.serialize_mode_title_node(&input_modes);
+ let mode_keybinds = self.serialize_mode_keybinds(&keybinds);
+ mode_node.set_children(mode_keybinds);
+ keybinds_children.nodes_mut().push(mode_node);
+ }
+ keybinds_node.set_children(keybinds_children);
+ keybinds_node
+ }
+}
+
+impl KeyWithModifier {
+ pub fn to_kdl(&self) -> String {
+ if self.key_modifiers.is_empty() {
+ self.bare_key.to_kdl()
+ } else {
+ format!(
+ "{} {}",
+ self.key_modifiers
+ .iter()
+ .map(|m| m.to_string())
+ .collect::<Vec<_>>()
+ .join(" "),
+ self.bare_key.to_kdl()
+ )
+ }
+ }
+}
+
+impl BareKey {
+ pub fn to_kdl(&self) -> String {
+ match self {
+ BareKey::PageDown => format!("PageDown"),
+ BareKey::PageUp => format!("PageUp"),
+ BareKey::Left => format!("left"),
+ BareKey::Down => format!("down"),
+ BareKey::Up => format!("up"),
+ BareKey::Right => format!("right"),
+ BareKey::Home => format!("home"),
+ BareKey::End => format!("end"),
+ BareKey::Backspace => format!("backspace"),
+ BareKey::Delete => format!("del"),
+ BareKey::Insert => format!("insert"),
+ BareKey::F(index) => format!("F{}", index),
+ BareKey::Char(' ') => format!("space"),
+ BareKey::Char(character) => format!("{}", character),
+ BareKey::Tab => format!("tab"),
+ BareKey::Esc => format!("esc"),
+ BareKey::Enter => format!("enter"),
+ BareKey::CapsLock => format!("capslock"),
+ BareKey::ScrollLock => format!("scrolllock"),
+ BareKey::NumLock => format!("numlock"),
+ BareKey::PrintScreen => format!("printscreen"),
+ BareKey::Pause => format!("pause"),
+ BareKey::Menu => format!("menu"),
+ }
+ }
}
impl Config {
@@ -1920,7 +3530,8 @@ impl Config {
config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds, &config.options)?;
}
if let Some(kdl_themes) = kdl_config.get("themes") {
- let config_themes = Themes::from_kdl(kdl_themes)?;
+ let sourced_from_external_file = false;
+ let config_themes = Themes::from_kdl(kdl_themes, sourced_from_external_file)?;
config.themes = config.themes.merge(config_themes);
}
if let Some(kdl_plugin_aliases) = kdl_config.get("plugins") {
@@ -1937,6 +3548,34 @@ impl Config {
}
Ok(config)
}
+ pub fn to_string(&self, add_comments: bool) -> String {
+ let mut document = KdlDocument::new();
+
+ let clear_defaults = true;
+ let keybinds = self.keybinds.to_kdl(clear_defaults);
+ document.nodes_mut().push(keybinds);
+
+ if let Some(themes) = self.themes.to_kdl() {
+ document.nodes_mut().push(themes);
+ }
+
+ let plugins = self.plugins.to_kdl();
+ document.nodes_mut().push(plugins);
+
+ if let Some(ui_config) = self.ui.to_kdl() {
+ document.nodes_mut().push(ui_config);
+ }
+
+ if let Some(env) = self.env.to_kdl() {
+ document.nodes_mut().push(env);
+ }
+
+ document
+ .nodes_mut()
+ .append(&mut self.options.to_kdl(add_comments));
+
+ document.to_string()
+ }
}
impl PluginAliases {
@@ -1962,6 +3601,46 @@ impl PluginAliases {
}
Ok(PluginAliases { aliases })
}
+ pub fn to_kdl(&self) -> KdlNode {
+ let mut plugins = KdlNode::new("plugins");
+ let mut plugins_children = KdlDocument::new();
+ for (alias_name, plugin_alias) in self.aliases.iter() {
+ let mut plugin_alias_node = KdlNode::new(alias_name.clone());
+ let mut plugin_alias_children = KdlDocument::new();
+ let location_string = plugin_alias.location.display();
+
+ plugin_alias_node.insert("location", location_string);
+ let cwd = plugin_alias.initial_cwd.as_ref();
+ let mut has_children = false;
+ if let Some(cwd) = cwd {
+ has_children = true;
+ let mut cwd_node = KdlNode::new("cwd");
+ cwd_node.push(cwd.display().to_string());
+ plugin_alias_children.nodes_mut().push(cwd_node);
+ }
+ let configuration = plugin_alias.configuration.inner();
+ if !configuration.is_empty() {
+ has_children = true;
+ for (config_key, config_value) in configuration {
+ let mut node = KdlNode::new(config_key.to_owned());
+ if config_value == "true" {
+ node.push(KdlValue::Bool(true));
+ } else if config_value == "false" {
+ node.push(KdlValue::Bool(false));
+ } else {
+ node.push(config_value.to_string());
+ }
+ plugin_alias_children.nodes_mut().push(node);
+ }
+ }
+ if has_children {
+ plugin_alias_node.set_children(plugin_alias_children);
+ }
+ plugins_children.nodes_mut().push(plugin_alias_node);
+ }
+ plugins.set_children(plugins_children);
+ plugins
+ }
}
impl UiConfig {
@@ -1981,10 +3660,40 @@ impl UiConfig {
}
Ok(ui_config)
}
+ pub fn to_kdl(&self) -> Option<KdlNode> {
+ let mut ui_config = KdlNode::new("ui");
+ let mut ui_config_children = KdlDocument::new();
+ let mut frame_config = KdlNode::new("pane_frames");
+ let mut frame_config_children = KdlDocument::new();
+ let mut has_ui_config = false;
+ if self.pane_frames.rounded_corners {
+ has_ui_config = true;
+ let mut rounded_corners = KdlNode::new("rounded_corners");
+ rounded_corners.push(KdlValue::Bool(true));
+ frame_config_children.nodes_mut().push(rounded_corners);
+ }
+ if self.pane_frames.hide_session_name {
+ has_ui_config = true;
+ let mut hide_session_name = KdlNode::new("hide_session_name");
+ hide_session_name.push(KdlValue::Bool(true));
+ frame_config_children.nodes_mut().push(hide_session_name);
+ }
+ if has_ui_config {
+ frame_config.set_children(frame_config_children);
+ ui_config_children.nodes_mut().push(frame_config);
+ ui_config.set_children(ui_config_children);
+ Some(ui_config)
+ } else {
+ None
+ }
+ }
}
impl Themes {
- pub fn from_kdl(themes_from_kdl: &KdlNode) -> Result<Self, ConfigError> {
+ pub fn from_kdl(
+ themes_from_kdl: &KdlNode,
+ sourced_from_external_file: bool,
+ ) -> Result<Self, ConfigError> {
let mut themes: HashMap<String, Theme> = HashMap::new();
for theme_config in kdl_children_nodes_or_error!(themes_from_kdl, "no themes found") {
let theme_name = kdl_name!(theme_config);
@@ -2004,6 +3713,7 @@ impl Themes {
white: PaletteColor::try_from(("white", theme_colors))?,
..Default::default()
},
+ sourced_from_external_file,
};
themes.insert(theme_name.into(), theme);
}
@@ -2011,14 +3721,17 @@ impl Themes {
Ok(themes)
}
- pub fn from_string(raw_string: &String) -> Result<Self, ConfigError> {
+ pub fn from_string(
+ raw_string: &String,
+ sourced_from_external_file: bool,
+ ) -> Result<Self, ConfigError> {
let kdl_config: KdlDocument = raw_string.parse()?;
let kdl_themes = kdl_config.get("themes").ok_or(ConfigError::new_kdl_error(
"No theme node found in file".into(),
kdl_config.span().offset(),
kdl_config.span().len(),
))?;
- let all_themes_in_file = Themes::from_kdl(kdl_themes)?;
+ let all_themes_in_file = Themes::from_kdl(kdl_themes, sourced_from_external_file)?;
Ok(all_themes_in_file)
}
@@ -2026,7 +3739,8 @@ impl Themes {
// String is the theme name
let kdl_config = std::fs::read_to_string(&path_to_theme_file)
.map_err(|e| ConfigError::IoPath(e, path_to_theme_file.clone()))?;
- Themes::from_string(&kdl_config).map_err(|e| match e {
+ let sourced_from_external_file = true;
+ Themes::from_string(&kdl_config, sourced_from_external_file).map_err(|e| match e {
ConfigError::KdlError(kdl_error) => ConfigError::KdlError(
kdl_error.add_src(path_to_theme_file.display().to_string(), kdl_config),
),
@@ -2049,6 +3763,63 @@ impl Themes {
}
Ok(themes)
}
+ pub fn to_kdl(&self) -> Option<KdlNode> {
+ let mut theme_node = KdlNode::new("themes");
+ let mut themes = KdlDocument::new();
+ let mut has_themes = false;
+ let sorted_themes: BTreeMap<String, Theme> = self.inner().clone().into_iter().collect();
+ for (theme_name, theme) in sorted_themes {
+ if theme.sourced_from_external_file {
+ // we do not serialize themes that have been defined in external files so as not to
+ // clog up the configuration file definitions
+ continue;
+ }
+ has_themes = true;
+ let mut current_theme_node = KdlNode::new(theme_name.clone());
+ let mut current_theme_node_children = KdlDocument::new();
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.fg.to_kdl("fg"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.bg.to_kdl("bg"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.red.to_kdl("red"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.green.to_kdl("green"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.yellow.to_kdl("yellow"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.blue.to_kdl("blue"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.magenta.to_kdl("magenta"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.orange.to_kdl("orange"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.cyan.to_kdl("cyan"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.black.to_kdl("black"));
+ current_theme_node_children
+ .nodes_mut()
+ .push(theme.palette.white.to_kdl("white"));
+ current_theme_node.set_children(current_theme_node_children);
+ themes.nodes_mut().push(current_theme_node);
+ }
+ if has_themes {
+ theme_node.set_children(themes);
+ Some(theme_node)
+ } else {
+ None
+ }
+ }
}
impl PermissionCache {
@@ -2197,7 +3968,7 @@ impl SessionInfo {
LayoutInfo::File(name) => (name.clone(), "file"),
LayoutInfo::BuiltIn(name) => (name.clone(), "built-in"),
LayoutInfo::Url(url) => (url.clone(), "url"),
- LayoutInfo::Stringified(stringified) => ("stringified-layout".to_owned(), "N/A"),
+ LayoutInfo::Stringified(_stringified) => ("stringified-layout".to_owned(), "N/A"),
};
let mut layout_node = KdlNode::new(format!("{}", layout_name));
let layout_source = KdlEntry::new_prop("source", layout_source);
@@ -2707,3 +4478,879 @@ fn serialize_and_deserialize_session_info_with_data() {
assert_eq!(session_info, deserealized);
insta::assert_snapshot!(serialized);
}
+
+#[test]
+fn keybinds_to_string() {
+ let fake_config = r#"
+ keybinds {
+ normal {
+ bind "Ctrl g" { SwitchToMode "Locked"; }
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Keybinds::from_kdl(
+ document.get("keybinds").unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ let clear_defaults = true;
+ let serialized = Keybinds::to_kdl(&deserialized, clear_defaults);
+ let deserialized_from_serialized = Keybinds::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("keybinds")
+ .unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ insta::assert_snapshot!(serialized.to_string());
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+}
+
+#[test]
+fn keybinds_to_string_without_clearing_defaults() {
+ let fake_config = r#"
+ keybinds {
+ normal {
+ bind "Ctrl g" { SwitchToMode "Locked"; }
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Keybinds::from_kdl(
+ document.get("keybinds").unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ let clear_defaults = false;
+ let serialized = Keybinds::to_kdl(&deserialized, clear_defaults);
+ let deserialized_from_serialized = Keybinds::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("keybinds")
+ .unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ insta::assert_snapshot!(serialized.to_string());
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+}
+
+#[test]
+fn keybinds_to_string_with_multiple_actions() {
+ let fake_config = r#"
+ keybinds {
+ normal {
+ bind "Ctrl n" { NewPane; SwitchToMode "Locked"; }
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Keybinds::from_kdl(
+ document.get("keybinds").unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ let clear_defaults = true;
+ let serialized = Keybinds::to_kdl(&deserialized, clear_defaults);
+ let deserialized_from_serialized = Keybinds::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("keybinds")
+ .unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn keybinds_to_string_with_all_actions() {
+ let fake_config = r#"
+ keybinds {
+ normal {
+ bind "Ctrl a" { Quit; }
+ bind "Ctrl b" { Write 102 111 111; }
+ bind "Ctrl c" { WriteChars "hi there!"; }
+ bind "Ctrl d" { SwitchToMode "Locked"; }
+ bind "Ctrl e" { Resize "Increase"; }
+ bind "Ctrl f" { FocusNextPane; }
+ bind "Ctrl g" { FocusPreviousPane; }
+ bind "Ctrl h" { SwitchFocus; }
+ bind "Ctrl i" { MoveFocus "Right"; }
+ bind "Ctrl j" { MoveFocusOrTab "Right"; }
+ bind "Ctrl k" { MovePane "Right"; }
+ bind "Ctrl l" { MovePaneBackwards; }
+ bind "Ctrl m" { Resize "Decrease Down"; }
+ bind "Ctrl n" { DumpScreen "/tmp/dumped"; }
+ bind "Ctrl o" { DumpLayout "/tmp/dumped-layout"; }
+ bind "Ctrl p" { EditScrollback; }
+ bind "Ctrl q" { ScrollUp; }
+ bind "Ctrl r" { ScrollDown; }
+ bind "Ctrl s" { ScrollToBottom; }
+ bind "Ctrl t" { ScrollToTop; }
+ bind "Ctrl u" { PageScrollUp; }
+ bind "Ctrl v" { PageScrollDown; }
+ bind "Ctrl w" { HalfPageScrollUp; }
+ bind "Ctrl x" { HalfPageScrollDown; }
+ bind "Ctrl y" { ToggleFocusFullscreen; }
+ bind "Ctrl z" { TogglePaneFrames; }
+ bind "Alt a" { ToggleActiveSyncTab; }
+ bind "Alt b" { NewPane "Right"; }
+ bind "Alt c" { TogglePaneEmbedOrFloating; }
+ bind "Alt d" { ToggleFloatingPanes; }
+ bind "Alt e" { CloseFocus; }
+ bind "Alt f" { PaneNameInput 0; }
+ bind "Alt g" { UndoRenamePane; }
+ bind "Alt h" { NewTab; }
+ bind "Alt i" { GoToNextTab; }
+ bind "Alt j" { GoToPreviousTab; }
+ bind "Alt k" { CloseTab; }
+ bind "Alt l" { GoToTab 1; }
+ bind "Alt m" { ToggleTab; }
+ bind "Alt n" { TabNameInput 0; }
+ bind "Alt o" { UndoRenameTab; }
+ bind "Alt p" { MoveTab "Right"; }
+ bind "Alt q" {
+ Run "ls" "-l" {
+ hold_on_start true;
+ hold_on_close false;
+ cwd "/tmp";
+ name "my cool pane";
+ };
+ }
+ bind "Alt r" {
+ Run "ls" "-l" {
+ hold_on_start true;
+ hold_on_close false;
+ cwd "/tmp";
+ name "my cool pane";
+ floating true;
+ };
+ }
+ bind "Alt s" {
+ Run "ls" "-l" {
+ hold_on_start true;
+ hold_on_close false;
+ cwd "/tmp";
+ name "my cool pane";
+ in_place true;
+ };
+ }
+ bind "Alt t" { Detach; }
+ bind "Alt u" {
+ LaunchOrFocusPlugin "zellij:session-manager"{
+ floating true;
+ move_to_focused_tab true;
+ skip_plugin_cache true;
+ config_key_1 "config_value_1";
+ config_key_2 "config_value_2";
+ };
+ }
+ bind "Alt v" {
+ LaunchOrFocusPlugin "zellij:session-manager"{
+ in_place true;
+ move_to_focused_tab true;
+ skip_plugin_cache true;
+ config_key_1 "config_value_1";
+ config_key_2 "config_value_2";
+ };
+ }
+ bind "Alt w" {
+ LaunchPlugin "zellij:session-manager" {
+ floating true;
+ skip_plugin_cache true;
+ config_key_1 "config_value_1";
+ config_key_2 "config_value_2";
+ };
+ }
+ bind "Alt x" {
+ LaunchPlugin "zellij:session-manager"{
+ in_place true;
+ skip_plugin_cache true;
+ config_key_1 "config_value_1";
+ config_key_2 "config_value_2";
+ };
+ }
+ bind "Alt y" { Copy; }
+ bind "Alt z" { SearchInput 0; }
+ bind "Ctrl Alt a" { Search "Up"; }
+ bind "Ctrl Alt b" { SearchToggleOption "CaseSensitivity"; }
+ bind "Ctrl Alt c" { ToggleMouseMode; }
+ bind "Ctrl Alt d" { PreviousSwapLayout; }
+ bind "Ctrl Alt e" { NextSwapLayout; }
+ bind "Ctrl Alt g" { BreakPane; }
+ bind "Ctrl Alt h" { BreakPaneRight; }
+ bind "Ctrl Alt i" { BreakPaneLeft; }
+ bind "Ctrl Alt i" { BreakPaneLeft; }
+ bind "Ctrl Alt j" {
+ MessagePlugin "zellij:session-manager"{
+ name "message_name";
+ payload "message_payload";
+ cwd "/tmp";
+ launch_new true;
+ skip_cache true;
+ floating true;
+ title "plugin_title";
+ config_key_1 "config_value_1";
+ config_key_2 "config_value_2";
+ };
+ }
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Keybinds::from_kdl(
+ document.get("keybinds").unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ let clear_defaults = true;
+ let serialized = Keybinds::to_kdl(&deserialized, clear_defaults);
+ let deserialized_from_serialized = Keybinds::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("keybinds")
+ .unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ // uncomment the below lines for more easily debugging a failed assertion here
+ // for (input_mode, input_mode_keybinds) in deserialized.0 {
+ // if let Some(other_input_mode_keybinds) = deserialized_from_serialized.0.get(&input_mode) {
+ // for (keybind, action) in input_mode_keybinds {
+ // if let Some(other_action) = other_input_mode_keybinds.get(&keybind) {
+ // assert_eq!(&action, other_action);
+ // } else {
+ // eprintln!("keybind: {:?} not found in other", keybind);
+ // }
+ // }
+ // }
+ // }
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn keybinds_to_string_with_shared_modes() {
+ let fake_config = r#"
+ keybinds {
+ normal {
+ bind "Ctrl n" { NewPane; SwitchToMode "Locked"; }
+ }
+ locked {
+ bind "Ctrl n" { NewPane; SwitchToMode "Locked"; }
+ }
+ shared_except "locked" "pane" {
+ bind "Ctrl f" { TogglePaneEmbedOrFloating; }
+ }
+ shared_among "locked" "pane" {
+ bind "Ctrl p" { WriteChars "foo"; }
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Keybinds::from_kdl(
+ document.get("keybinds").unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ let clear_defaults = true;
+ let serialized = Keybinds::to_kdl(&deserialized, clear_defaults);
+ let deserialized_from_serialized = Keybinds::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("keybinds")
+ .unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn keybinds_to_string_with_multiple_multiline_actions() {
+ let fake_config = r#"
+ keybinds {
+ shared {
+ bind "Ctrl n" {
+ NewPane
+ SwitchToMode "Locked"
+ MessagePlugin "zellij:session-manager"{
+ name "message_name";
+ payload "message_payload";
+ cwd "/tmp";
+ launch_new true;
+ skip_cache true;
+ floating true;
+ title "plugin_title";
+ config_key_1 "config_value_1";
+ config_key_2 "config_value_2";
+ };
+ }
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Keybinds::from_kdl(
+ document.get("keybinds").unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ let clear_defaults = true;
+ let serialized = Keybinds::to_kdl(&deserialized, clear_defaults);
+ let deserialized_from_serialized = Keybinds::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("keybinds")
+ .unwrap(),
+ Default::default(),
+ &Default::default(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn themes_to_string() {
+ let fake_config = r#"
+ themes {
+ dracula {
+ fg 248 248 242
+ bg 40 42 54
+ black 0 0 0
+ red 255 85 85
+ green 80 250 123
+ yellow 241 250 140
+ blue 98 114 164
+ magenta 255 121 198
+ cyan 139 233 253
+ white 255 255 255
+ orange 255 184 108
+ }
+ }"#;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let sourced_from_external_file = false;
+ let deserialized =
+ Themes::from_kdl(document.get("themes").unwrap(), sourced_from_external_file).unwrap();
+ let serialized = Themes::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = Themes::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("themes")
+ .unwrap(),
+ sourced_from_external_file,
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn themes_to_string_with_hex_definitions() {
+ let fake_config = r##"
+ themes {
+ nord {
+ fg "#D8DEE9"
+ bg "#2E3440"
+ black "#3B4252"
+ red "#BF616A"
+ green "#A3BE8C"
+ yellow "#EBCB8B"
+ blue "#81A1C1"
+ magenta "#B48EAD"
+ cyan "#88C0D0"
+ white "#E5E9F0"
+ orange "#D08770"
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let sourced_from_external_file = false;
+ let deserialized =
+ Themes::from_kdl(document.get("themes").unwrap(), sourced_from_external_file).unwrap();
+ let serialized = Themes::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = Themes::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("themes")
+ .unwrap(),
+ sourced_from_external_file,
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn themes_to_string_with_eight_bit_definitions() {
+ let fake_config = r##"
+ themes {
+ default {
+ fg 1
+ bg 10
+ black 20
+ red 30
+ green 40
+ yellow 50
+ blue 60
+ magenta 70
+ cyan 80
+ white 90
+ orange 254
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let sourced_from_external_file = false;
+ let deserialized =
+ Themes::from_kdl(document.get("themes").unwrap(), sourced_from_external_file).unwrap();
+ let serialized = Themes::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = Themes::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("themes")
+ .unwrap(),
+ sourced_from_external_file,
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn themes_to_string_with_combined_definitions() {
+ let fake_config = r##"
+ themes {
+ default {
+ fg 1
+ bg 10
+ black 20
+ red 30
+ green 40
+ yellow 50
+ blue 60
+ magenta 70
+ cyan 80
+ white 255 255 255
+ orange "#D08770"
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let sourced_from_external_file = false;
+ let deserialized =
+ Themes::from_kdl(document.get("themes").unwrap(), sourced_from_external_file).unwrap();
+ let serialized = Themes::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = Themes::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("themes")
+ .unwrap(),
+ sourced_from_external_file,
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn themes_to_string_with_multiple_theme_definitions() {
+ let fake_config = r##"
+ themes {
+ nord {
+ fg "#D8DEE9"
+ bg "#2E3440"
+ black "#3B4252"
+ red "#BF616A"
+ green "#A3BE8C"
+ yellow "#EBCB8B"
+ blue "#81A1C1"
+ magenta "#B48EAD"
+ cyan "#88C0D0"
+ white "#E5E9F0"
+ orange "#D08770"
+ }
+ dracula {
+ fg 248 248 242
+ bg 40 42 54
+ black 0 0 0
+ red 255 85 85
+ green 80 250 123
+ yellow 241 250 140
+ blue 98 114 164
+ magenta 255 121 198
+ cyan 139 233 253
+ white 255 255 255
+ orange 255 184 108
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let sourced_from_external_file = false;
+ let deserialized =
+ Themes::from_kdl(document.get("themes").unwrap(), sourced_from_external_file).unwrap();
+ let serialized = Themes::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = Themes::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("themes")
+ .unwrap(),
+ sourced_from_external_file,
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn plugins_to_string() {
+ let fake_config = r##"
+ plugins {
+ tab-bar location="zellij:tab-bar"
+ status-bar location="zellij:status-bar"
+ strider location="zellij:strider"
+ compact-bar location="zellij:compact-bar"
+ session-manager location="zellij:session-manager"
+ welcome-screen location="zellij:session-manager" {
+ welcome_screen true
+ }
+ filepicker location="zellij:strider" {
+ cwd "/"
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = PluginAliases::from_kdl(document.get("plugins").unwrap()).unwrap();
+ let serialized = PluginAliases::to_kdl(&deserialized);
+ let deserialized_from_serialized = PluginAliases::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("plugins")
+ .unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn plugins_to_string_with_file_and_web() {
+ let fake_config = r##"
+ plugins {
+ tab-bar location="https://foo.com/plugin.wasm"
+ filepicker location="file:/path/to/my/plugin.wasm" {
+ cwd "/"
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = PluginAliases::from_kdl(document.get("plugins").unwrap()).unwrap();
+ let serialized = PluginAliases::to_kdl(&deserialized);
+ let deserialized_from_serialized = PluginAliases::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("plugins")
+ .unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn ui_config_to_string() {
+ let fake_config = r##"
+ ui {
+ pane_frames {
+ rounded_corners true
+ hide_session_name true
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = UiConfig::from_kdl(document.get("ui").unwrap()).unwrap();
+ let serialized = UiConfig::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = UiConfig::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("ui")
+ .unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn ui_config_to_string_with_no_ui_config() {
+ let fake_config = r##"
+ ui {
+ pane_frames {
+ }
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = UiConfig::from_kdl(document.get("ui").unwrap()).unwrap();
+ assert_eq!(UiConfig::to_kdl(&deserialized), None);
+}
+
+#[test]
+fn env_vars_to_string() {
+ let fake_config = r##"
+ env {
+ foo "bar"
+ bar "foo"
+ thing 1
+ baz "true"
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = EnvironmentVariables::from_kdl(document.get("env").unwrap()).unwrap();
+ let serialized = EnvironmentVariables::to_kdl(&deserialized).unwrap();
+ let deserialized_from_serialized = EnvironmentVariables::from_kdl(
+ serialized
+ .to_string()
+ .parse::<KdlDocument>()
+ .unwrap()
+ .get("env")
+ .unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(serialized.to_string());
+}
+
+#[test]
+fn env_vars_to_string_with_no_env_vars() {
+ let fake_config = r##"
+ env {
+ }"##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = EnvironmentVariables::from_kdl(document.get("env").unwrap()).unwrap();
+ assert_eq!(EnvironmentVariables::to_kdl(&deserialized), None);
+}
+
+#[test]
+fn config_options_to_string() {
+ let fake_config = r##"
+ simplified_ui true
+ theme "dracula"
+ default_mode "locked"
+ default_shell "fish"
+ default_cwd "/tmp/foo"
+ default_layout "compact"
+ layout_dir "/tmp/layouts"
+ theme_dir "/tmp/themes"
+ mouse_mode false
+ pane_frames false
+ mirror_session true
+ on_force_close "quit"
+ scroll_buffer_size 100
+ copy_command "pbcopy"
+ copy_clipboard "system"
+ copy_on_select false
+ scrollback_editor "vim"
+ session_name "my_cool_session"
+ attach_to_session false
+ auto_layout false
+ session_serialization true
+ serialize_pane_viewport false
+ scrollback_lines_to_serialize 1000
+ styled_underlines false
+ serialization_interval 1
+ disable_session_metadata true
+ support_kitty_keyboard_protocol false
+ "##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Options::from_kdl(&document).unwrap();
+ let mut serialized = Options::to_kdl(&deserialized, false);
+ let mut fake_document = KdlDocument::new();
+ fake_document.nodes_mut().append(&mut serialized);
+ let deserialized_from_serialized =
+ Options::from_kdl(&fake_document.to_string().parse::<KdlDocument>().unwrap()).unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(fake_document.to_string());
+}
+
+#[test]
+fn config_options_to_string_with_comments() {
+ let fake_config = r##"
+ simplified_ui true
+ theme "dracula"
+ default_mode "locked"
+ default_shell "fish"
+ default_cwd "/tmp/foo"
+ default_layout "compact"
+ layout_dir "/tmp/layouts"
+ theme_dir "/tmp/themes"
+ mouse_mode false
+ pane_frames false
+ mirror_session true
+ on_force_close "quit"
+ scroll_buffer_size 100
+ copy_command "pbcopy"
+ copy_clipboard "system"
+ copy_on_select false
+ scrollback_editor "vim"
+ session_name "my_cool_session"
+ attach_to_session false
+ auto_layout false
+ session_serialization true
+ serialize_pane_viewport false
+ scrollback_lines_to_serialize 1000
+ styled_underlines false
+ serialization_interval 1
+ disable_session_metadata true
+ support_kitty_keyboard_protocol false
+ "##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Options::from_kdl(&document).unwrap();
+ let mut serialized = Options::to_kdl(&deserialized, true);
+ let mut fake_document = KdlDocument::new();
+ fake_document.nodes_mut().append(&mut serialized);
+ let deserialized_from_serialized =
+ Options::from_kdl(&fake_document.to_string().parse::<KdlDocument>().unwrap()).unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(fake_document.to_string());
+}
+
+#[test]
+fn config_options_to_string_without_options() {
+ let fake_config = r##"
+ "##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Options::from_kdl(&document).unwrap();
+ let mut serialized = Options::to_kdl(&deserialized, false);
+ let mut fake_document = KdlDocument::new();
+ fake_document.nodes_mut().append(&mut serialized);
+ let deserialized_from_serialized =
+ Options::from_kdl(&fake_document.to_string().parse::<KdlDocument>().unwrap()).unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(fake_document.to_string());
+}
+
+#[test]
+fn config_options_to_string_with_some_options() {
+ let fake_config = r##"
+ default_layout "compact"
+ "##;
+ let document: KdlDocument = fake_config.parse().unwrap();
+ let deserialized = Options::from_kdl(&document).unwrap();
+ let mut serialized = Options::to_kdl(&deserialized, false);
+ let mut fake_document = KdlDocument::new();
+ fake_document.nodes_mut().append(&mut serialized);
+ let deserialized_from_serialized =
+ Options::from_kdl(&fake_document.to_string().parse::<KdlDocument>().unwrap()).unwrap();
+ assert_eq!(
+ deserialized, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(fake_document.to_string());
+}
+
+#[test]
+fn bare_config_from_default_assets_to_string() {
+ let fake_config = Config::from_default_assets().unwrap();
+ let fake_config_stringified = fake_config.to_string(false);
+ let deserialized_from_serialized = Config::from_kdl(&fake_config_stringified, None).unwrap();
+ assert_eq!(
+ fake_config, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(fake_config_stringified);
+}
+
+#[test]
+fn bare_config_from_default_assets_to_string_with_comments() {
+ let fake_config = Config::from_default_assets().unwrap();
+ let fake_config_stringified = fake_config.to_string(true);
+ let deserialized_from_serialized = Config::from_kdl(&fake_config_stringified, None).unwrap();
+ assert_eq!(
+ fake_config, deserialized_from_serialized,
+ "Deserialized serialized config equals original config"
+ );
+ insta::assert_snapshot!(fake_config_stringified);
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap
new file mode 100644
index 00000000..6cd58d55
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap
@@ -0,0 +1,240 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 5060
+expression: fake_config_stringified
+---
+keybinds clear-defaults=true {
+ locked {
+ bind "Ctrl g" { SwitchToMode "normal"; }
+ }
+ pane {
+ bind "left" { MoveFocus "left"; }
+ bind "down" { MoveFocus "down"; }
+ bind "up" { MoveFocus "up"; }
+ bind "right" { MoveFocus "right"; }
+ bind "c" { SwitchToMode "renamepane"; PaneNameInput 0; }
+ bind "d" { NewPane "down"; SwitchToMode "normal"; }
+ bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; }
+ bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; }
+ bind "h" { MoveFocus "left"; }
+ bind "j" { MoveFocus "down"; }
+ bind "k" { MoveFocus "up"; }
+ bind "l" { MoveFocus "right"; }
+ bind "n" { NewPane; SwitchToMode "normal"; }
+ bind "p" { SwitchFocus; }
+ bind "Ctrl p" { SwitchToMode "normal"; }
+ bind "r" { NewPane "right"; SwitchToMode "normal"; }
+ bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; }
+ bind "z" { TogglePaneFrames; SwitchToMode "normal"; }
+ }
+ tab {
+ bind "left" { GoToPreviousTab; }
+ bind "down" { GoToNextTab; }
+ bind "up" { GoToPreviousTab; }
+ bind "right" { GoToNextTab; }
+ bind "1" { GoToTab 1; SwitchToMode "normal"; }
+ bind "2" { GoToTab 2; SwitchToMode "normal"; }
+ bind "3" { GoToTab 3; SwitchToMode "normal"; }
+ bind "4" { GoToTab 4; SwitchToMode "normal"; }
+ bind "5" { GoToTab 5; SwitchToMode "normal"; }
+ bind "6" { GoToTab 6; SwitchToMode "normal"; }
+ bind "7" { GoToTab 7; SwitchToMode "normal"; }
+ bind "8" { GoToTab 8; SwitchToMode "normal"; }
+ bind "9" { GoToTab 9; SwitchToMode "normal"; }
+ bind "[" { BreakPaneLeft; SwitchToMode "normal"; }
+ bind "]" { BreakPaneRight; SwitchToMode "normal"; }
+ bind "b" { BreakPane; SwitchToMode "normal"; }
+ bind "h" { GoToPreviousTab; }
+ bind "j" { GoToNextTab; }
+ bind "k" { GoToPreviousTab; }
+ bind "l" { GoToNextTab; }
+ bind "n" { NewTab; SwitchToMode "normal"; }
+ bind "r" { SwitchToMode "renametab"; TabNameInput 0; }
+ bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; }
+ bind "Ctrl t" { SwitchToMode "normal"; }
+ bind "x" { CloseTab; SwitchToMode "normal"; }
+ bind "tab" { ToggleTab; }
+ }
+ resize {
+ bind "left" { Resize "Increase left"; }
+ bind "down" { Resize "Increase down"; }
+ bind "up" { Resize "Increase up"; }
+ bind "right" { Resize "Increase right"; }
+ bind "+" { Resize "Increase"; }
+ bind "-" { Resize "Decrease"; }
+ bind "=" { Resize "Increase"; }
+ bind "H" { Resize "Decrease left"; }
+ bind "J" { Resize "Decrease down"; }
+ bind "K" { Resize "Decrease up"; }
+ bind "L" { Resize "Decrease right"; }
+ bind "h" { Resize "Increase left"; }
+ bind "j" { Resize "Increase down"; }
+ bind "k" { Resize "Increase up"; }
+ bind "l" { Resize "Increase right"; }
+ bind "Ctrl n" { SwitchToMode "normal"; }
+ }
+ move {
+ bind "left" { MovePane "left"; }
+ bind "down" { MovePane "down"; }
+ bind "up" { MovePane "up"; }
+ bind "right" { MovePane "right"; }
+ bind "h" { MovePane "left"; }
+ bind "Ctrl h" { SwitchToMode "normal"; }
+ bind "j" { MovePane "down"; }
+ bind "k" { MovePane "up"; }
+ bind "l" { MovePane "right"; }
+ bind "n" { MovePane; }
+ bind "p" { MovePaneBackwards; }
+ bind "tab" { MovePane; }
+ }
+ scroll {
+ bind "e" { EditScrollback; SwitchToMode "normal"; }
+ bind "s" { SwitchToMode "entersearch"; SearchInput 0; }
+ }
+ search {
+ bind "c" { SearchToggleOption "CaseSensitivity"; }
+ bind "n" { Search "down"; }
+ bind "o" { SearchToggleOption "WholeWord"; }
+ bind "p" { Search "up"; }
+ bind "w" { SearchToggleOption "Wrap"; }
+ }
+ session {
+ bind "c" {
+ LaunchOrFocusPlugin "configuration" {
+ floating true
+ move_to_focused_tab true
+ }
+ SwitchToMode "normal"
+ }
+ bind "Ctrl o" { SwitchToMode "normal"; }
+ bind "w" {
+ LaunchOrFocusPlugin "session-manager" {
+ floating true
+ move_to_focused_tab true
+ }
+ SwitchToMode "normal"
+ }
+ }
+ shared_except "locked" {
+ bind "Alt left" { MoveFocusOrTab "left"; }
+ bind "Alt down" { MoveFocus "down"; }
+ bind "Alt up" { MoveFocus "up"; }
+ bind "Alt right" { MoveFocusOrTab "right"; }
+ bind "Alt +" { Resize "Increase"; }
+ bind "Alt -" { Resize "Decrease"; }
+ bind "Alt =" { Resize "Increase"; }
+ bind "Alt [" { PreviousSwapLayout; }
+ bind "Alt ]" { NextSwapLayout; }
+ bind "Alt f" { ToggleFloatingPanes; }
+ bind "Ctrl g" { SwitchToMode "locked"; }
+ bind "Alt h" { MoveFocusOrTab "left"; }
+ bind "Alt i" { MoveTab "left"; }
+ bind "Alt j" { MoveFocus "down"; }
+ bind "Alt k" { MoveFocus "up"; }
+ bind "Alt l" { MoveFocusOrTab "right"; }
+ bind "Alt n" { NewPane; }
+ bind "Alt o" { MoveTab "right"; }
+ bind "Ctrl q" { Quit; }
+ }
+ shared_except "locked" "move" {
+ bind "Ctrl h" { SwitchToMode "move"; }
+ }
+ shared_except "locked" "session" {
+ bind "Ctrl o" { SwitchToMode "session"; }
+ }
+ shared_except "locked" "scroll" "search" "tmux" {
+ bind "Ctrl b" { SwitchToMode "tmux"; }
+ }
+ shared_except "locked" "scroll" "search" {
+ bind "Ctrl s" { SwitchToMode "scroll"; }
+ }
+ shared_except "locked" "tab" {
+ bind "Ctrl t" { SwitchToMode "tab"; }
+ }
+ shared_except "locked" "pane" {
+ bind "Ctrl p" { SwitchToMode "pane"; }
+ }
+ shared_except "locked" "resize" {
+ bind "Ctrl n" { SwitchToMode "resize"; }
+ }
+ shared_except "normal" "locked" "entersearch" {
+ bind "enter" { SwitchToMode "normal"; }
+ }
+ shared_except "normal" "locked" "entersearch" "renametab" "renamepane" {
+ bind "esc" { SwitchToMode "normal"; }
+ }
+ shared_among "pane" "tmux" {
+ bind "x" { CloseFocus; SwitchToMode "normal"; }
+ }
+ shared_among "scroll" "search" {
+ bind "PageDown" { PageScrollDown; }
+ bind "PageUp" { PageScrollUp; }
+ bind "left" { PageScrollUp; }
+ bind "down" { ScrollDown; }
+ bind "up" { ScrollUp; }
+ bind "right" { PageScrollDown; }
+ bind "Ctrl b" { PageScrollUp; }
+ bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; }
+ bind "d" { HalfPageScrollDown; }
+ bind "Ctrl f" { PageScrollDown; }
+ bind "h" { PageScrollUp; }
+ bind "j" { ScrollDown; }
+ bind "k" { ScrollUp; }
+ bind "l" { PageScrollDown; }
+ bind "Ctrl s" { SwitchToMode "normal"; }
+ bind "u" { HalfPageScrollUp; }
+ }
+ entersearch {
+ bind "Ctrl c" { SwitchToMode "scroll"; }
+ bind "esc" { SwitchToMode "scroll"; }
+ bind "enter" { SwitchToMode "search"; }
+ }
+ renametab {
+ bind "esc" { UndoRenameTab; SwitchToMode "tab"; }
+ }
+ shared_among "renametab" "renamepane" {
+ bind "Ctrl c" { SwitchToMode "normal"; }
+ }
+ renamepane {
+ bind "esc" { UndoRenamePane; SwitchToMode "pane"; }
+ }
+ shared_among "session" "tmux" {
+ bind "d" { Detach; }
+ }
+ tmux {
+ bind "left" { MoveFocus "left"; SwitchToMode "normal"; }
+ bind "down" { MoveFocus "down"; SwitchToMode "normal"; }
+ bind "up" { MoveFocus "up"; SwitchToMode "normal"; }
+ bind "right" { MoveFocus "right"; SwitchToMode "normal"; }
+ bind "space" { NextSwapLayout; }
+ bind "\"" { NewPane "down"; SwitchToMode "normal"; }
+ bind "%" { NewPane "right"; SwitchToMode "normal"; }
+ bind "," { SwitchToMode "renametab"; }
+ bind "[" { SwitchToMode "scroll"; }
+ bind "Ctrl b" { Write 2; SwitchToMode "normal"; }
+ bind "c" { NewTab; SwitchToMode "normal"; }
+ bind "h" { MoveFocus "left"; SwitchToMode "normal"; }
+ bind "j" { MoveFocus "down"; SwitchToMode "normal"; }
+ bind "k" { MoveFocus "up"; SwitchToMode "normal"; }
+ bind "l" { MoveFocus "right"; SwitchToMode "normal"; }
+ bind "n" { GoToNextTab; SwitchToMode "normal"; }
+ bind "o" { FocusNextPane; }
+ bind "p" { GoToPreviousTab; SwitchToMode "normal"; }
+ bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; }
+ }
+}
+plugins {
+ compact-bar location="zellij:compact-bar"
+ configuration location="zellij:configuration"
+ filepicker location="zellij:strider" {
+ cwd "/"
+ }
+ session-manager location="zellij:session-manager"
+ status-bar location="zellij:status-bar"
+ strider location="zellij:strider"
+ tab-bar location="zellij:tab-bar"
+ welcome-screen location="zellij:session-manager" {
+ welcome_screen true
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap
new file mode 100644
index 00000000..443f1017
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap
@@ -0,0 +1,409 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 5069
+expression: fake_config_stringified
+---
+keybinds clear-defaults=true {
+ locked {
+ bind "Ctrl g" { SwitchToMode "normal"; }
+ }
+ pane {
+ bind "left" { MoveFocus "left"; }
+ bind "down" { MoveFocus "down"; }
+ bind "up" { MoveFocus "up"; }
+ bind "right" { MoveFocus "right"; }
+ bind "c" { SwitchToMode "renamepane"; PaneNameInput 0; }
+ bind "d" { NewPane "down"; SwitchToMode "normal"; }
+ bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; }
+ bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; }
+ bind "h" { MoveFocus "left"; }
+ bind "j" { MoveFocus "down"; }
+ bind "k" { MoveFocus "up"; }
+ bind "l" { MoveFocus "right"; }
+ bind "n" { NewPane; SwitchToMode "normal"; }
+ bind "p" { SwitchFocus; }
+ bind "Ctrl p" { SwitchToMode "normal"; }
+ bind "r" { NewPane "right"; SwitchToMode "normal"; }
+ bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; }
+ bind "z" { TogglePaneFrames; SwitchToMode "normal"; }
+ }
+ tab {
+ bind "left" { GoToPreviousTab; }
+ bind "down" { GoToNextTab; }
+ bind "up" { GoToPreviousTab; }
+ bind "right" { GoToNextTab; }
+ bind "1" { GoToTab 1; SwitchToMode "normal"; }
+ bind "2" { GoToTab 2; SwitchToMode "normal"; }
+ bind "3" { GoToTab 3; SwitchToMode "normal"; }
+ bind "4" { GoToTab 4; SwitchToMode "normal"; }
+ bind "5" { GoToTab 5; SwitchToMode "normal"; }
+ bind "6" { GoToTab 6; SwitchToMode "normal"; }
+ bind "7" { GoToTab 7; SwitchToMode "normal"; }
+ bind "8" { GoToTab 8; SwitchToMode "normal"; }
+ bind "9" { GoToTab 9; SwitchToMode "normal"; }
+ bind "[" { BreakPaneLeft; SwitchToMode "normal"; }
+ bind "]" { BreakPaneRight; SwitchToMode "normal"; }
+ bind "b" { BreakPane; SwitchToMode "normal"; }
+ bind "h" { GoToPreviousTab; }
+ bind "j" { GoToNextTab; }
+ bind "k" { GoToPreviousTab; }
+ bind "l" { GoToNextTab; }
+ bind "n" { NewTab; SwitchToMode "normal"; }
+ bind "r" { SwitchToMode "renametab"; TabNameInput 0; }
+ bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; }
+ bind "Ctrl t" { SwitchToMode "normal"; }
+ bind "x" { CloseTab; SwitchToMode "normal"; }
+ bind "tab" { ToggleTab; }
+ }
+ resize {
+ bind "left" { Resize "Increase left"; }
+ bind "down" { Resize "Increase down"; }
+ bind "up" { Resize "Increase up"; }
+ bind "right" { Resize "Increase right"; }
+ bind "+" { Resize "Increase"; }
+ bind "-" { Resize "Decrease"; }
+ bind "=" { Resize "Increase"; }
+ bind "H" { Resize "Decrease left"; }
+ bind "J" { Resize "Decrease down"; }
+ bind "K" { Resize "Decrease up"; }
+ bind "L" { Resize "Decrease right"; }
+ bind "h" { Resize "Increase left"; }
+ bind "j" { Resize "Increase down"; }
+ bind "k" { Resize "Increase up"; }
+ bind "l" { Resize "Increase right"; }
+ bind "Ctrl n" { SwitchToMode "normal"; }
+ }
+ move {
+ bind "left" { MovePane "left"; }
+ bind "down" { MovePane "down"; }
+ bind "up" { MovePane "up"; }
+ bind "right" { MovePane "right"; }
+ bind "h" { MovePane "left"; }
+ bind "Ctrl h" { SwitchToMode "normal"; }
+ bind "j" { MovePane "down"; }
+ bind "k" { MovePane "up"; }
+ bind "l" { MovePane "right"; }
+ bind "n" { MovePane; }
+ bind "p" { MovePaneBackwards; }
+ bind "tab" { MovePane; }
+ }
+ scroll {
+ bind "e" { EditScrollback; SwitchToMode "normal"; }
+ bind "s" { SwitchToMode "entersearch"; SearchInput 0; }
+ }
+ search {
+ bind "c" { SearchToggleOption "CaseSensitivity"; }
+ bind "n" { Search "down"; }
+ bind "o" { SearchToggleOption "WholeWord"; }
+ bind "p" { Search "up"; }
+ bind "w" { SearchToggleOption "Wrap"; }
+ }
+ session {
+ bind "c" {
+ LaunchOrFocusPlugin "configuration" {
+ floating true
+ move_to_focused_tab true
+ }
+ SwitchToMode "normal"
+ }
+ bind "Ctrl o" { SwitchToMode "normal"; }
+ bind "w" {
+ LaunchOrFocusPlugin "session-manager" {
+ floating true
+ move_to_focused_tab true
+ }
+ SwitchToMode "normal"
+ }
+ }
+ shared_except "locked" {
+ bind "Alt left" { MoveFocusOrTab "left"; }
+ bind "Alt down" { MoveFocus "down"; }
+ bind "Alt up" { MoveFocus "up"; }
+ bind "Alt right" { MoveFocusOrTab "right"; }
+ bind "Alt +" { Resize "Increase"; }
+ bind "Alt -" { Resize "Decrease"; }
+ bind "Alt =" { Resize "Increase"; }
+ bind "Alt [" { PreviousSwapLayout; }
+ bind "Alt ]" { NextSwapLayout; }
+ bind "Alt f" { ToggleFloatingPanes; }
+ bind "Ctrl g" { SwitchToMode "locked"; }
+ bind "Alt h" { MoveFocusOrTab "left"; }
+ bind "Alt i" { MoveTab "left"; }
+ bind "Alt j" { MoveFocus "down"; }
+ bind "Alt k" { MoveFocus "up"; }
+ bind "Alt l" { MoveFocusOrTab "right"; }
+ bind "Alt n" { NewPane; }
+ bind "Alt o" { MoveTab "right"; }
+ bind "Ctrl q" { Quit; }
+ }
+ shared_except "locked" "move" {
+ bind "Ctrl h" { SwitchToMode "move"; }
+ }
+ shared_except "locked" "session" {
+ bind "Ctrl o" { SwitchToMode "session"; }
+ }
+ shared_except "locked" "scroll" "search" "tmux" {
+ bind "Ctrl b" { SwitchToMode "tmux"; }
+ }
+ shared_except "locked" "scroll" "search" {
+ bind "Ctrl s" { SwitchToMode "scroll"; }
+ }
+ shared_except "locked" "tab" {
+ bind "Ctrl t" { SwitchToMode "tab"; }
+ }
+ shared_except "locked" "pane" {
+ bind "Ctrl p" { SwitchToMode "pane"; }
+ }
+ shared_except "locked" "resize" {
+ bind "Ctrl n" { SwitchToMode "resize"; }
+ }
+ shared_except "normal" "locked" "entersearch" {
+ bind "enter" { SwitchToMode "normal"; }
+ }
+ shared_except "normal" "locked" "entersearch" "renametab" "renamepane" {
+ bind "esc" { SwitchToMode "normal"; }
+ }
+ shared_among "pane" "tmux" {
+ bind "x" { CloseFocus; SwitchToMode "normal"; }
+ }
+ shared_among "scroll" "search" {
+ bind "PageDown" { PageScrollDown; }
+ bind "PageUp" { PageScrollUp; }
+ bind "left" { PageScrollUp; }
+ bind "down" { ScrollDown; }
+ bind "up" { ScrollUp; }
+ bind "right" { PageScrollDown; }
+ bind "Ctrl b" { PageScrollUp; }
+ bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; }
+ bind "d" { HalfPageScrollDown; }
+ bind "Ctrl f" { PageScrollDown; }
+ bind "h" { PageScrollUp; }
+ bind "j" { ScrollDown; }
+ bind "k" { ScrollUp; }
+ bind "l" { PageScrollDown; }
+ bind "Ctrl s" { SwitchToMode "normal"; }
+ bind "u" { HalfPageScrollUp; }
+ }
+ entersearch {
+ bind "Ctrl c" { SwitchToMode "scroll"; }
+ bind "esc" { SwitchToMode "scroll"; }
+ bind "enter" { SwitchToMode "search"; }
+ }
+ renametab {
+ bind "esc" { UndoRenameTab; SwitchToMode "tab"; }
+ }
+ shared_among "renametab" "renamepane" {
+ bind "Ctrl c" { SwitchToMode "normal"; }
+ }
+ renamepane {
+ bind "esc" { UndoRenamePane; SwitchToMode "pane"; }
+ }
+ shared_among "session" "tmux" {
+ bind "d" { Detach; }
+ }
+ tmux {
+ bind "left" { MoveFocus "left"; SwitchToMode "normal"; }
+ bind "down" { MoveFocus "down"; SwitchToMode "normal"; }
+ bind "up" { MoveFocus "up"; SwitchToMode "normal"; }
+ bind "right" { MoveFocus "right"; SwitchToMode "normal"; }
+ bind "space" { NextSwapLayout; }
+ bind "\"" { NewPane "down"; SwitchToMode "normal"; }
+ bind "%" { NewPane "right"; SwitchToMode "normal"; }
+ bind "," { SwitchToMode "renametab"; }
+ bind "[" { SwitchToMode "scroll"; }
+ bind "Ctrl b" { Write 2; SwitchToMode "normal"; }
+ bind "c" { NewTab; SwitchToMode "normal"; }
+ bind "h" { MoveFocus "left"; SwitchToMode "normal"; }
+ bind "j" { MoveFocus "down"; SwitchToMode "normal"; }
+ bind "k" { MoveFocus "up"; SwitchToMode "normal"; }
+ bind "l" { MoveFocus "right"; SwitchToMode "normal"; }
+ bind "n" { GoToNextTab; SwitchToMode "normal"; }
+ bind "o" { FocusNextPane; }
+ bind "p" { GoToPreviousTab; SwitchToMode "normal"; }
+ bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; }
+ }
+}
+plugins {
+ compact-bar location="zellij:compact-bar"
+ configuration location="zellij:configuration"
+ filepicker location="zellij:strider" {
+ cwd "/"
+ }
+ session-manager location="zellij:session-manager"
+ status-bar location="zellij:status-bar"
+ strider location="zellij:strider"
+ tab-bar location="zellij:tab-bar"
+ welcome-screen location="zellij:session-manager" {
+ welcome_screen true
+ }
+}
+
+// Use a simplified UI without special fonts (arrow glyphs)
+// Options:
+// - true
+// - false (Default)
+//
+// simplified_ui true
+
+// Choose the theme that is specified in the themes section.
+// Default: default
+//
+// theme "dracula"
+
+// Choose the base input mode of zellij.
+// Default: normal
+//
+// default_mode "locked"
+
+// Choose the path to the default shell that zellij will use for opening new panes
+// Default: $SHELL
+//
+// default_shell "fish"
+
+// Choose the path to override cwd that zellij will use for opening new panes
+//
+// default_cwd "/tmp"
+
+// The name of the default layout to load on startup
+// Default: "default"
+//
+// default_layout "compact"
+
+// The folder in which Zellij will look for layouts
+//
+// layout_dir "/tmp"
+
+// The folder in which Zellij will look for themes
+//
+// theme_dir "/tmp"
+
+// Toggle enabling the mouse mode.
+// On certain configurations, or terminals this could
+// potentially interfere with copying text.
+// Options:
+// - true (default)
+// - false
+//
+// mouse_mode false
+
+// Toggle having pane frames around the panes
+// Options:
+// - true (default, enabled)
+// - false
+//
+// pane_frames false
+
+// 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
+//
+// mirror_session true
+
+// 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
+//
+// on_force_close "quit"
+
+// 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
+//
+// scroll_buffer_size 10000
+
+// 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
+//
+// copy_command "pbcopy"
+
+// 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
+//
+// copy_clipboard "primary"
+
+// Enable automatic copying (and clearing) of selection when releasing mouse
+// Default: true
+//
+// copy_on_select true
+
+// Path to the default editor to use to edit pane scrollbuffer
+// Default: $EDITOR or $VISUAL
+// scrollback_editor "/usr/bin/vim"
+
+// A fixed name to always give the Zellij session.
+// Consider also setting `attach_to_session true,`
+// otherwise this will error if such a session exists.
+// Default: <RANDOM>
+//
+// session_name "My singleton session"
+
+// When `session_name` is provided, attaches to that session
+// if it is already running or creates it otherwise.
+// Default: false
+//
+// attach_to_session true
+
+// Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible
+// Options:
+// - true (default)
+// - false
+//
+// auto_layout false
+
+// Whether sessions should be serialized to the cache folder (including their tabs/panes, cwds and running commands) so that they can later be resurrected
+// Options:
+// - true (default)
+// - false
+//
+// session_serialization false
+
+// Whether pane viewports are serialized along with the session, default is false
+// Options:
+// - true
+// - false (default)
+//
+// serialize_pane_viewport false
+
+// 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. This does nothing if `serialize_pane_viewport` is not true.
+//
+// scrollback_lines_to_serialize 10000
+
+// Enable or disable the rendering of styled and colored underlines (undercurl).
+// May need to be disabled for certain unsupported terminals
+// Default: true
+//
+// styled_underlines false
+
+// How often in seconds sessions are serialized
+//
+// serialization_interval 10000
+
+// Enable or disable writing of session metadata to disk (if disabled, other sessions might not know
+// metadata info on this session)
+// Default: false
+//
+// disable_session_metadata false
+
+// Enable or disable support for the enhanced Kitty Keyboard Protocol (the host terminal must also support it)
+// Default: true (if the host terminal supports it)
+//
+// support_kitty_keyboard_protocol false
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string.snap
new file mode 100644
index 00000000..e7f95a87
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string.snap
@@ -0,0 +1,33 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 4182
+expression: fake_document.to_string()
+---
+simplified_ui true
+theme "dracula"
+default_mode "locked"
+default_shell "fish"
+default_cwd "/tmp/foo"
+default_layout "compact"
+layout_dir "/tmp/layouts"
+theme_dir "/tmp/themes"
+mouse_mode false
+pane_frames false
+mirror_session true
+on_force_close "quit"
+scroll_buffer_size 100
+copy_command "pbcopy"
+copy_clipboard "system"
+copy_on_select false
+scrollback_editor "vim"
+session_name "my_cool_session"
+attach_to_session false
+auto_layout false
+session_serialization true
+serialize_pane_viewport false
+scrollback_lines_to_serialize 1000
+styled_underlines false
+serialization_interval 1
+disable_session_metadata true
+support_kitty_keyboard_protocol false
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap
new file mode 100644
index 00000000..3d0736e0
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap
@@ -0,0 +1,175 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 4987
+expression: fake_document.to_string()
+---
+
+// Use a simplified UI without special fonts (arrow glyphs)
+// Options:
+// - true
+// - false (Default)
+//
+simplified_ui true
+
+// Choose the theme that is specified in the themes section.
+// Default: default
+//
+theme "dracula"
+
+// Choose the base input mode of zellij.
+// Default: normal
+//
+default_mode "locked"
+
+// Choose the path to the default shell that zellij will use for opening new panes
+// Default: $SHELL
+//
+default_shell "fish"
+
+// Choose the path to override cwd that zellij will use for opening new panes
+//
+default_cwd "/tmp/foo"
+
+// The name of the default layout to load on startup
+// Default: "default"
+//
+default_layout "compact"
+
+// The folder in which Zellij will look for layouts
+//
+layout_dir "/tmp/layouts"
+
+// The folder in which Zellij will look for themes
+//
+theme_dir "/tmp/themes"
+
+// Toggle enabling the mouse mode.
+// On certain configurations, or terminals this could
+// potentially interfere with copying text.
+// Options:
+// - true (default)
+// - false
+//
+mouse_mode false
+
+// Toggle having pane frames around the panes
+// Options:
+// - true (default, enabled)
+// - false
+//
+pane_frames false
+
+// 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
+//
+mirror_session true
+
+// 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
+//
+on_force_close "quit"
+
+// 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
+//
+scroll_buffer_size 100
+
+// 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
+//
+copy_command "pbcopy"
+
+// 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
+//
+copy_clipboard "system"
+
+// Enable automatic copying (and clearing) of selection when releasing mouse
+// Default: true
+//
+copy_on_select false
+
+// Path to the default editor to use to edit pane scrollbuffer
+// Default: $EDITOR or $VISUAL
+scrollback_editor "vim"
+
+// A fixed name to always give the Zellij session.
+// Consider also setting `attach_to_session true,`
+// otherwise this will error if such a session exists.
+// Default: <RANDOM>
+//
+session_name "my_cool_session"
+
+// When `session_name` is provided, attaches to that session
+// if it is already running or creates it otherwise.
+// Default: false
+//
+attach_to_session false
+
+// Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible
+// Options:
+// - true (default)
+// - false
+//
+auto_layout false
+
+// Whether sessions should be serialized to the cache folder (including their tabs/panes, cwds and running commands) so that they can later be resurrected
+// Options:
+// - true (default)
+// - false
+//
+session_serialization true
+
+// Whether pane viewports are serialized along with the session, default is false
+// Options:
+// - true
+// - false (default)
+//
+serialize_pane_viewport false
+
+// 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. This does nothing if `serialize_pane_viewport` is not true.
+//
+scrollback_lines_to_serialize 1000
+
+// Enable or disable the rendering of styled and colored underlines (undercurl).
+// May need to be disabled for certain unsupported terminals
+// Default: true
+//
+styled_underlines false
+
+// How often in seconds sessions are serialized
+//
+serialization_interval 1
+
+// Enable or disable writing of session metadata to disk (if disabled, other sessions might not know
+// metadata info on this session)
+// Default: false
+//
+disable_session_metadata true
+
+// Enable or disable support for the enhanced Kitty Keyboard Protocol (the host terminal must also support it)
+// Default: true (if the host terminal supports it)
+//
+support_kitty_keyboard_protocol false
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_some_options.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_some_options.snap
new file mode 100644
index 00000000..2cf4ba5b
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_some_options.snap
@@ -0,0 +1,7 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 4211
+expression: fake_document.to_string()
+---
+default_layout "compact"
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_without_options.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_without_options.snap
new file mode 100644
index 00000000..82eaf8e5
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_without_options.snap
@@ -0,0 +1,6 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 4196
+expression: fake_document.to_string()
+---
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__env_vars_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__env_vars_to_string.snap
new file mode 100644
index 00000000..609ed896
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__env_vars_to_string.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3981
+expression: serialized.to_string()
+---
+env {
+ bar "foo"
+ baz "true"
+ foo "bar"
+ thing "1"
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string.snap
new file mode 100644
index 00000000..3055ea87
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 2558
+expression: serialized.to_string()
+---
+keybinds clear-defaults=true {
+ normal {
+ bind "Ctrl g" { SwitchToMode "locked"; }
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap
new file mode 100644
index 00000000..43e85faa
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap
@@ -0,0 +1,130 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 2731
+expression: serialized.to_string()
+---
+keybinds clear-defaults=true {
+ normal {
+ bind "Ctrl a" { Quit; }
+ bind "Ctrl Alt a" { Search "up"; }
+ bind "Alt a" { ToggleActiveSyncTab; }
+ bind "Ctrl b" { Write 102 111 111; }
+ bind "Ctrl Alt b" { SearchToggleOption "CaseSensitivity"; }
+ bind "Alt b" { NewPane "right"; }
+ bind "Ctrl c" { WriteChars "hi there!"; }
+ bind "Ctrl Alt c" { ToggleMouseMode; }
+ bind "Alt c" { TogglePaneEmbedOrFloating; }
+ bind "Ctrl d" { SwitchToMode "locked"; }
+ bind "Ctrl Alt d" { PreviousSwapLayout; }
+ bind "Alt d" { ToggleFloatingPanes; }
+ bind "Ctrl e" { Resize "Increase"; }
+ bind "Ctrl Alt e" { NextSwapLayout; }
+ bind "Alt e" { CloseFocus; }
+ bind "Ctrl f" { FocusNextPane; }
+ bind "Alt f" { PaneNameInput 0; }
+ bind "Ctrl g" { FocusPreviousPane; }
+ bind "Ctrl Alt g" { BreakPane; }
+ bind "Alt g" { UndoRenamePane; }
+ bind "Ctrl h" { SwitchFocus; }
+ bind "Ctrl Alt h" { BreakPaneRight; }
+ bind "Alt h" { NewTab; }
+ bind "Ctrl i" { MoveFocus "right"; }
+ bind "Ctrl Alt i" { BreakPaneLeft; }
+ bind "Alt i" { GoToNextTab; }
+ bind "Ctrl j" { MoveFocusOrTab "right"; }
+ bind "Ctrl Alt j" {
+ MessagePlugin "zellij:session-manager" {
+ name "message_name"
+ cwd "/tmp"
+ payload "message_payload"
+ launch_new true
+ skip_cache true
+ floating true
+ title "plugin_title"
+ config_key_1 "config_value_1"
+ config_key_2 "config_value_2"
+ }
+ }
+ bind "Alt j" { GoToPreviousTab; }
+ bind "Ctrl k" { MovePane "right"; }
+ bind "Alt k" { CloseTab; }
+ bind "Ctrl l" { MovePaneBackwards; }
+ bind "Alt l" { GoToTab 1; }
+ bind "Ctrl m" { Resize "Decrease down"; }
+ bind "Alt m" { ToggleTab; }
+ bind "Ctrl n" { DumpScreen "/tmp/dumped"; }
+ bind "Alt n" { TabNameInput 0; }
+ bind "Ctrl o" { DumpLayout; }
+ bind "Alt o" { UndoRenameTab; }
+ bind "Ctrl p" { EditScrollback; }
+ bind "Alt p" { MoveTab "right"; }
+ bind "Ctrl q" { ScrollUp; }
+ bind "Alt q" {
+ Run "ls" "-l" {
+ cwd "/tmp"
+ name "my cool pane"
+ }
+ }
+ bind "Ctrl r" { ScrollDown; }
+ bind "Alt r" {
+ Run "ls" "-l" {
+ floating true
+ cwd "/tmp"
+ name "my cool pane"
+ }
+ }
+ bind "Ctrl s" { ScrollToBottom; }
+ bind "Alt s" {
+ Run "ls" "-l" {
+ in_place true
+ cwd "/tmp"
+ name "my cool pane"
+ }
+ }
+ bind "Ctrl t" { ScrollToTop; }
+ bind "Alt t" { Detach; }
+ bind "Ctrl u" { PageScrollUp; }
+ bind "Alt u" {
+ LaunchOrFocusPlugin "zellij:session-manager" {
+ floating true
+ move_to_focused_tab true
+ skip_plugin_cache true
+ config_key_1 "config_value_1"
+ config_key_2 "config_value_2"
+ }
+ }
+ bind "Ctrl v" { PageScrollDown; }
+ bind "Alt v" {
+ LaunchOrFocusPlugin "zellij:session-manager" {
+ move_to_focused_tab true
+ in_place true
+ skip_plugin_cache true
+ config_key_1 "config_value_1"
+ config_key_2 "config_value_2"
+ }
+ }
+ bind "Ctrl w" { HalfPageScrollUp; }
+ bind "Alt w" {
+ LaunchPlugin "zellij:session-manager" {
+ floating true
+ skip_plugin_cache true
+ config_key_1 "config_value_1"
+ config_key_2 "config_value_2"
+ }
+ }
+ bind "Ctrl x" { HalfPageScrollDown; }
+ bind "Alt x" {
+ LaunchPlugin "zellij:session-manager" {
+ in_place true
+ skip_plugin_cache true
+ config_key_1 "config_value_1"
+ config_key_2 "config_value_2"
+ }
+ }
+ bind "Ctrl y" { ToggleFocusFullscreen; }
+ bind "Alt y" { Copy; }
+ bind "Ctrl z" { TogglePaneFrames; }
+ bind "Alt z" { SearchInput 0; }
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_actions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_actions.snap
new file mode 100644
index 00000000..ddaa4c0d
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_actions.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 2576
+expression: serialized.to_string()
+---
+keybinds clear-defaults=true {
+ normal {
+ bind "Ctrl n" { NewPane; SwitchToMode "locked"; }
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_multiline_actions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_multiline_actions.snap
new file mode 100644
index 00000000..d6a1792c
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_multiple_multiline_actions.snap
@@ -0,0 +1,25 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 2788
+expression: serialized.to_string()
+---
+keybinds clear-defaults=true {
+ shared {
+ bind "Ctrl n" {
+ NewPane
+ SwitchToMode "locked"
+ MessagePlugin "zellij:session-manager" {
+ name "message_name"
+ cwd "/tmp"
+ payload "message_payload"
+ launch_new true
+ skip_cache true
+ floating true
+ title "plugin_title"
+ config_key_1 "config_value_1"
+ config_key_2 "config_value_2"
+ }
+ }
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_shared_modes.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_shared_modes.snap
new file mode 100644
index 00000000..d7ca3ad2
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_shared_modes.snap
@@ -0,0 +1,17 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 2757
+expression: serialized.to_string()
+---
+keybinds clear-defaults=true {
+ shared_among "normal" "locked" {
+ bind "Ctrl n" { NewPane; SwitchToMode "locked"; }
+ }
+ shared_except "locked" "pane" {
+ bind "Ctrl f" { TogglePaneEmbedOrFloating; }
+ }
+ shared_among "locked" "pane" {
+ bind "Ctrl p" { WriteChars "foo"; }
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_without_clearing_defaults.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_without_clearing_defaults.snap
new file mode 100644
index 00000000..143bb1ef
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_without_clearing_defaults.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 2575
+expression: serialized.to_string()
+---
+keybinds {
+ normal {
+ bind "Ctrl g" { SwitchToMode "locked"; }
+ }
+}
+
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string.snap
new file mode 100644
index 00000000..694f2c15
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string.snap
@@ -0,0 +1,18 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3874
+expression: serialized.to_string()
+---
+plugins {
+ compact-bar location="zellij:compact-bar"
+ filepicker location="zellij:strider" {
+ cwd "/"
+ }
+ session-manager location="zellij:session-manager"
+ status-bar location="zellij:status-bar"
+ strider location="zellij:strider"
+ tab-bar location="zellij:tab-bar"
+ welcome-screen location="zellij:session-manager" {
+ welcome_screen true
+ }
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string_with_file_and_web.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string_with_file_and_web.snap
new file mode 100644
index 00000000..4f998225
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__plugins_to_string_with_file_and_web.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3891
+expression: serialized.to_string()
+---
+plugins {
+ filepicker location="file:/path/to/my/plugin.wasm" {
+ cwd "/"
+ }
+ tab-bar location="https://foo.com/plugin.wasm"
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap
new file mode 100644
index 00000000..7bbc19cd
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap
@@ -0,0 +1,20 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3697
+expression: serialized.to_string()
+---
+themes {
+ dracula {
+ fg 248 248 242
+ bg 40 42 54
+ red 255 85 85
+ green 80 250 123
+ yellow 241 250 140
+ blue 98 114 164
+ magenta 255 121 198
+ orange 255 184 108
+ cyan 139 233 253
+ black 0 0 0
+ white 255 255 255
+ }
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap
new file mode 100644
index 00000000..3498af78
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap
@@ -0,0 +1,20 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3775
+expression: serialized.to_string()
+---
+themes {
+ default {
+ fg 1
+ bg 10
+ red 30
+ green 40
+ yellow 50
+ blue 60
+ magenta 70
+ orange 208 135 112
+ cyan 80
+ black 20
+ white 255 255 255
+ }
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap
new file mode 100644
index 00000000..5406cbb8
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap
@@ -0,0 +1,20 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3749
+expression: serialized.to_string()
+---
+themes {
+ default {
+ fg 1
+ bg 10
+ red 30
+ green 40
+ yellow 50
+ blue 60
+ magenta 70
+ orange 254
+ cyan 80
+ black 20
+ white 90
+ }
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap
new file mode 100644
index 00000000..3cbf559a
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap
@@ -0,0 +1,20 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3723
+expression: serialized.to_string()
+---
+themes {
+ nord {
+ fg 216 222 233
+ bg 46 52 64
+ red 191 97 106
+ green 163 190 140
+ yellow 235 203 139
+ blue 129 161 193
+ magenta 180 142 173
+ orange 208 135 112
+ cyan 136 192 208
+ black 59 66 82
+ white 229 233 240
+ }
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap
new file mode 100644
index 00000000..f8d7d38b
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap
@@ -0,0 +1,33 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 4821
+expression: serialized.to_string()
+---
+themes {
+ dracula {
+ fg 248 248 242
+ bg 40 42 54
+ red 255 85 85
+ green 80 250 123
+ yellow 241 250 140
+ blue 98 114 164
+ magenta 255 121 198
+ orange 255 184 108
+ cyan 139 233 253
+ black 0 0 0
+ white 255 255 255
+ }
+ nord {
+ fg 216 222 233
+ bg 46 52 64
+ red 191 97 106
+ green 163 190 140
+ yellow 235 203 139
+ blue 129 161 193
+ magenta 180 142 173
+ orange 208 135 112
+ cyan 136 192 208
+ black 59 66 82
+ white 229 233 240
+ }
+}
diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__ui_config_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__ui_config_to_string.snap
new file mode 100644
index 00000000..5956ba92
--- /dev/null
+++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__ui_config_to_string.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-utils/src/kdl/mod.rs
+assertion_line: 3932
+expression: serialized.to_string()
+---
+ui {
+ pane_frames {
+ rounded_corners true
+ hide_session_name true
+ }
+}
diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto
index 610a3ac4..a985806f 100644
--- a/zellij-utils/src/plugin_api/event.proto
+++ b/zellij-utils/src/plugin_api/event.proto
@@ -48,6 +48,7 @@ enum EventType {
EditPaneOpened = 22;
EditPaneExited = 23;
CommandPaneReRun = 24;
+ FailedToWriteConfigToDisk = 25;
}
message EventNameList {
@@ -77,9 +78,14 @@ message Event {
EditPaneOpenedPayload edit_pane_opened_payload = 19;
EditPaneExitedPayload edit_pane_exited_payload = 20;
CommandPaneReRunPayload command_pane_rerun_payload = 21;
+ FailedToWriteConfigToDiskPayload failed_to_write_config_to_disk_payload = 22;
}
}
+message FailedToWriteConfigToDiskPayload {
+ optional string file_path = 1;
+}
+
message CommandPaneReRunPayload {
uint32 terminal_pane_id = 1;
repeated ContextItem context = 3;
diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs
index b669a672..faeb53d2 100644
--- a/zellij-utils/src/plugin_api/event.rs
+++ b/zellij-utils/src/plugin_api/event.rs
@@ -312,6 +312,14 @@ impl TryFrom<ProtobufEvent> for Event {
},
_ => Err("Malformed payload for the CommandPaneReRun Event"),
},
+ Some(ProtobufEventType::FailedToWriteConfigToDisk) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::FailedToWriteConfigToDiskPayload(
+ failed_to_write_configuration_payload,
+ )) => Ok(Event::FailedToWriteConfigToDisk(
+ failed_to_write_configuration_payload.file_path,
+ )),
+ _ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
+ },
None => Err("Unknown Protobuf Event"),
}
}
@@ -620,6 +628,12 @@ impl TryFrom<Event> for ProtobufEvent {
)),
})
},
+ Event::FailedToWriteConfigToDisk(file_path) => Ok(ProtobufEvent {
+ name: ProtobufEventType::FailedToWriteConfigToDisk as i32,
+ payload: Some(event::Payload::FailedToWriteConfigToDiskPayload(
+ FailedToWriteConfigToDiskPayload { file_path },
+ )),
+ }),
}
}
}
@@ -1129,6 +1143,7 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::EditPaneOpened => EventType::EditPaneOpened,
ProtobufEventType::EditPaneExited => EventType::EditPaneExited,
ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
+ ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
})
}
}
@@ -1162,6 +1177,7 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::EditPaneOpened => ProtobufEventType::EditPaneOpened,
EventType::EditPaneExited => ProtobufEventType::EditPaneExited,
EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
+ EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
})
}
}
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index d91cc817..be47f567 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -160,7 +160,7 @@ message PluginCommand {
KillSessionsPayload kill_sessions_payload = 60;
string scan_host_folder_payload = 61;
NewTabsWithLayoutInfoPayload new_tabs_with_layout_info_payload = 62;
- string reconfigure_payload = 63;
+ ReconfigurePayload reconfigure_payload = 63;
HidePaneWithIdPayload hide_pane_with_id_payload = 64;
ShowPaneWithIdPayload show_pane_with_id_payload = 65;
OpenCommandPanePayload open_command_pane_background_payload = 66;
@@ -168,6 +168,11 @@ message PluginCommand {
}
}
+message ReconfigurePayload {
+ string config = 1;
+ bool write_to_disk = 2;
+}
+
message RerunCommandPanePayload {
uint32 terminal_pane_id = 1;
}
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 3df40531..4ffabbe4 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -11,9 +11,9 @@ pub use super::generated_api::api::{
MovePayload, NewPluginArgs as ProtobufNewPluginArgs, NewTabsWithLayoutInfoPayload,
OpenCommandPanePayload, OpenFilePayload, PaneId as ProtobufPaneId,
PaneType as ProtobufPaneType, PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
- RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePayload, RunCommandPayload,
- SetTimeoutPayload, ShowPaneWithIdPayload, SubscribePayload, SwitchSessionPayload,
- SwitchTabToPayload, UnsubscribePayload, WebRequestPayload,
+ ReconfigurePayload, RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePayload,
+ RunCommandPayload, SetTimeoutPayload, ShowPaneWithIdPayload, SubscribePayload,
+ SwitchSessionPayload, SwitchTabToPayload, UnsubscribePayload, WebRequestPayload,
},
plugin_permission::PermissionType as ProtobufPermissionType,
resize::ResizeAction as ProtobufResizeAction,
@@ -935,7 +935,10 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
Some(CommandName::Reconfigure) => match protobuf_plugin_command.payload {
Some(Payload::ReconfigurePayload(reconfigure_payload)) => {
- Ok(PluginCommand::Reconfigure(reconfigure_payload))
+ Ok(PluginCommand::Reconfigure(
+ reconfigure_payload.config,
+ reconfigure_payload.write_to_disk,
+ ))
},
_ => Err("Mismatched payload for Reconfigure"),
},
@@ -1558,9 +1561,12 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
)),
})
},
- PluginCommand::Reconfigure(reconfigure_payload) => Ok(ProtobufPluginCommand {
+ PluginCommand::Reconfigure(config, write_to_disk) => Ok(ProtobufPluginCommand {
name: CommandName::Reconfigure as i32,
- payload: Some(Payload::ReconfigurePayload(reconfigure_payload)),
+ payload: Some(Payload::ReconfigurePayload(ReconfigurePayload {
+ config,
+ write_to_disk,
+ })),
}),
PluginCommand::HidePaneWithId(pane_id_to_hide) => Ok(ProtobufPluginCommand {
name: CommandName::HidePaneWithId as i32,
diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs
index 941c351b..de9547af 100644
--- a/zellij-utils/src/setup.rs
+++ b/zellij-utils/src/setup.rs
@@ -74,7 +74,8 @@ fn get_default_themes() -> Themes {
let mut themes = Themes::default();
for file in ZELLIJ_DEFAULT_THEMES.files() {
if let Some(content) = file.contents_utf8() {
- match Themes::from_string(&content.to_string()) {
+ let sourced_from_external_file = true;
+ match Themes::from_string(&content.to_string(), sourced_from_external_file) {
Ok(theme) => themes = themes.merge(theme),
Err(_) => {},
}
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
index 56cea06e..bfda7107 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
-assertion_line: 839
+assertion_line: 840
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -5671,6 +5671,7 @@ Config {
0,
),
},
+ sourced_from_external_file: false,
},
"theme-from-config": Theme {
palette: Palette {
@@ -5772,6 +5773,7 @@ Config {
0,
),
},
+ sourced_from_external_file: false,
},
"theme-from-layout": Theme {
palette: Palette {
@@ -5873,6 +5875,7 @@ Config {
0,
),
},
+ sourced_from_external_file: false,
},
},
plugins: PluginAliases {