From 2c09b230b62d50d6db85fedd118529501612ace9 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 15 Jul 2022 16:16:09 +0200 Subject: [PATCH 01/55] chore(config): default kdl keybindings config --- zellij-utils/assets/config/default.kdl | 240 +++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 zellij-utils/assets/config/default.kdl diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl new file mode 100644 index 0000000000..ed7accd326 --- /dev/null +++ b/zellij-utils/assets/config/default.kdl @@ -0,0 +1,240 @@ +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Ctrl s" { SwitchToMode "Scroll" } + bind "Ctrl o" { SwitchToMode "Session" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl b" { SwitchToMode "Tmux" } + bind "Ctrl q" { Quit } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt =" "Alt +" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + // uncomment this and adjust key if using copy_on_select=false + // bind "Alt c" { Copy } + } + locked { + bind "Ctrl g" { SwitchToMode "Normal" } + } + resize { + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal" } + bind "Ctrl s" { SwitchToMode "Scroll" } + bind "Ctrl o" { SwitchToMode "Session" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl b" { SwitchToMode "Tmux" } + bind "Ctrl q" { Quit } + bind "h" "Left" { Resize "Left" } + bind "j" "Down" { Resize "Down" } + bind "k" "Up" { Resize "Up" } + bind "l" "Right" { Resize "Right" } + bind "=" "+" { Resize "Increase" } + bind "-" { Resize "Decrease" } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + } + pane { + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal" } + bind "Ctrl s" { SwitchToMode "Scroll" } + bind "Ctrl o" { SwitchToMode "Session" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl b" { SwitchToMode "Tmux" } + bind "Ctrl q" { Quit } + bind "h" "Left" { MoveFocus "Left" } + bind "l" "Right" { MoveFocus "Right" } + bind "j" "Down" { MoveFocus "Down" } + bind "k" "Up" { MoveFocus "Up" } + bind "p" { SwitchFocus } + bind "n" { NewPane; SwitchToMode "Normal"; } + bind "d" { NewPane "Down"; SwitchToMode "Normal"; } + bind "r" { NewPane "Right; SwitchToMode "Normal"; } + bind "x" { CloseFocus; SwitchToMode "Normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "Normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "Normal" } + bind "w" { ToggleFloatingPanes; SwitchToMode "Normal" } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal" } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + bind "c" { SwitchToMode "RenamePane" } + } + move { + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal" } + bind "Ctrl s" { SwitchToMode "Scroll" } + bind "Ctrl o" { SwitchToMode "Session" } + bind "Ctrl q" { Quit } + bind "n" "Tab" { MovePane } + bind "h" "Left" { MovePane "Left" } + bind "j" "Down" { MovePane "Down" } + bind "k" "Up" { MovePane "Up" } + bind "l" "Right" { MovePane "Right" } + bind "Alt n" { NewPane } + bind "Alt h" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + } + tab { + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal" } + bind "Ctrl s" { SwitchToMode "Scroll" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl b" { SwitchToMode "Tmux" } + bind "Ctrl o" { SwitchToMode "Session" } + bind "Char r" { SwitchToMode "RenameTab" } + bind "Ctrl q" { Quit } + bind "h" "Left" "Up" "k" { GoToPreviousTab } + bind "l" "Right" "Down" "j" { GoToNextTab } + bind "n" { NewTab; SwitchToMode "Normal"; } + bind "x" { CloseTab; SwitchToMode "Normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "Normal"; } + 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 "Tab" { ToggleTab } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + } + scroll { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl b" { SwitchToMode "Tmux" } + bind "Ctrl o" { SwitchToMode "Session" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "e" { EditScrollback; SwitchToMode "Normal"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } + bind "Ctrl q" { Quit } + bind "j" "Down" { ScrollDown } + bind "k" "Up" { ScrollUp } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp } + bind "d" { HalfPageScrollDown } + bind "u" { HalfPageScrollUp } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + // uncomment this and adjust key if using copy_on_select=false + // bind "Alt c" { Copy } + } + renametab: + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal" } + bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt =" {Resize "Increase" } + bind "Alt +" {Resize "Increase" } + bind "Alt -" {Resize "Decrease" } + renamepane: + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal" } + bind "Esc" { UndoRenamePane; SwitchToMode "Pane" } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + session: + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl b" { SwitchToMode "Tmux" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal" } + bind "Ctrl s" { SwitchToMode "Scroll" } + bind "Ctrl q" { Quit } + bind "d" { Detach } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "Alt +" "Alt =" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + tmux { + bind "Ctrl g" { SwitchToMode "Locked" } + bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl p" { SwitchToMode "Pane" } + bind "Ctrl h" { SwitchToMode "Move" } + bind "Ctrl t" { SwitchToMode "Tab" } + bind "Enter" "Space" "Esc" { SwitchToMode Normal } + bind "[" { SwitchToMode "Scroll" } + bind "Ctrl q" { Quit } + bind "Ctrl b" { Write "Ctrl b"; SwitchToMode "Normal"; } + bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } + bind "%" { NewPane "Right"; SwitchToMode "Normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "Normal"; } + bind "c" { NewTab; SwitchToMode "Normal"; } + bind "," { SwitchToMode "RenameTab" } + bind "p" { GoToPreviousTab; SwitchToMode "Normal"; } + bind "n" { GoToNextTab; SwitchToMode "Normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "Normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "Normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "Normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "Normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "Normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "Normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "Normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "Normal"; } + bind "Alt n" { NewPane } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } + bind "Alt j" "Alt Down" { MoveFocus "Down" } + bind "Alt k" "Alt Up" { MoveFocus "Up" } + bind "o" { FocusNextPane } + bind "Alt =" { Resize "Increase" } + bind "Alt +" { Resize "Increase" } + bind "Alt -" { Resize "Decrease" } + bind "d" { Detach } + } +} From db49c307bd2077ced1b0e23aaef2359d0c198adc Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 5 Aug 2022 14:53:03 +0200 Subject: [PATCH 02/55] tests --- Cargo.lock | 93 +- zellij-client/src/fake_client.rs | 305 ++-- zellij-client/src/lib.rs | 17 +- zellij-utils/Cargo.toml | 2 + zellij-utils/assets/config/default.kdl | 528 ++++-- .../assets/config/test_config_deleteme.kdl | 376 ++++ zellij-utils/src/data.rs | 21 + zellij-utils/src/envs.rs | 9 +- zellij-utils/src/input/actions.rs | 28 + zellij-utils/src/input/config.rs | 1067 ++++++++++- zellij-utils/src/input/keybinds.rs | 147 +- zellij-utils/src/input/options.rs | 11 + zellij-utils/src/input/plugins.rs | 221 +-- zellij-utils/src/input/theme.rs | 13 +- zellij-utils/src/input/unit/keybinds_test.rs | 1614 +++++++++-------- zellij-utils/src/kdl.rs | 431 +++++ zellij-utils/src/lib.rs | 2 + zellij-utils/src/setup.rs | 202 ++- 18 files changed, 3571 insertions(+), 1516 deletions(-) create mode 100644 zellij-utils/assets/config/test_config_deleteme.kdl create mode 100644 zellij-utils/src/kdl.rs diff --git a/Cargo.lock b/Cargo.lock index 4a3d08ab51..a2df7f0efa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,6 +359,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "chumsky" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" + [[package]] name = "clap" version = "3.2.4" @@ -1225,6 +1231,44 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kdl" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0170e808d03465c564754ac497fe718aae33146b135a0f882a7ca4066257e0b" +dependencies = [ + "miette 4.7.1", + "nom 7.1.1", + "thiserror", +] + +[[package]] +name = "knuffel" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9f7a07459e9dc5d07f5dabfc2c2a965bf39195451eb785b61460c65f386eef" +dependencies = [ + "base64", + "chumsky", + "knuffel-derive", + "miette 4.7.1", + "thiserror", + "unicode-width", +] + +[[package]] +name = "knuffel-derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcbdb0b6f26a4e5ecb0dd9074a430398a41b2c1624c205bcc202541ddc15488" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1412,7 +1456,7 @@ checksum = "cd2adcfcced5d625bf90a958a82ae5b93231f57f3df1383fee28c9b5096d35ed" dependencies = [ "atty", "backtrace", - "miette-derive", + "miette-derive 3.3.0", "once_cell", "owo-colors", "supports-color", @@ -1423,6 +1467,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "miette" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c" +dependencies = [ + "miette-derive 4.7.1", + "once_cell", + "thiserror", + "unicode-width", +] + [[package]] name = "miette-derive" version = "3.3.0" @@ -1434,6 +1490,23 @@ dependencies = [ "syn", ] +[[package]] +name = "miette-derive" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.5.3" @@ -1503,6 +1576,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "ntapi" version = "0.3.7" @@ -2472,7 +2555,7 @@ checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e" dependencies = [ "dirs", "fnv", - "nom", + "nom 5.1.2", "phf", "phf_codegen", ] @@ -3273,7 +3356,7 @@ dependencies = [ "dialoguer", "insta", "log", - "miette", + "miette 3.3.0", "names", "rand 0.8.5", "ssh2", @@ -3356,11 +3439,13 @@ dependencies = [ "crossbeam", "directories-next", "interprocess", + "kdl", + "knuffel", "lazy_static", "libc", "log", "log4rs", - "miette", + "miette 3.3.0", "nix", "once_cell", "regex", diff --git a/zellij-client/src/fake_client.rs b/zellij-client/src/fake_client.rs index 88b41c9e89..e7e14b6443 100644 --- a/zellij-client/src/fake_client.rs +++ b/zellij-client/src/fake_client.rs @@ -28,156 +28,157 @@ pub fn start_fake_client( _layout: Option, actions: Vec, ) { - debug!("Starting fake Zellij client!"); - let session_name = info.get_session_name(); - - // TODO: Ideally the `fake_client` would not need to specify these options, - // but the `[NewTab:]` action depends on this state being - // even in this client. - let palette = config.themes.clone().map_or_else( - || os_input.load_palette(), - |t| { - t.theme_config(&config_options) - .unwrap_or_else(|| os_input.load_palette()) - }, - ); - - let full_screen_ws = os_input.get_terminal_size_using_fd(0); - let client_attributes = ClientAttributes { - size: full_screen_ws, - style: Style { - colors: palette, - rounded_corners: config.ui.unwrap_or_default().pane_frames.rounded_corners, - }, - }; - - let first_msg = ClientToServerMsg::AttachClient(client_attributes, config_options.clone()); - - let zellij_ipc_pipe: PathBuf = { - let mut sock_dir = zellij_utils::consts::ZELLIJ_SOCK_DIR.clone(); - fs::create_dir_all(&sock_dir).unwrap(); - zellij_utils::shared::set_permissions(&sock_dir, 0o700).unwrap(); - sock_dir.push(session_name); - sock_dir - }; - os_input.connect_to_server(&*zellij_ipc_pipe); - os_input.send_to_server(first_msg); - - let mut command_is_executing = CommandIsExecuting::new(); - - let (send_client_instructions, receive_client_instructions): ChannelWithContext< - ClientInstruction, - > = channels::bounded(50); - let send_client_instructions = SenderWithContext::new(send_client_instructions); - - let (send_input_instructions, receive_input_instructions): ChannelWithContext< - InputInstruction, - > = channels::bounded(50); - let send_input_instructions = SenderWithContext::new(send_input_instructions); - - std::panic::set_hook({ - use zellij_utils::errors::handle_panic; - let send_client_instructions = send_client_instructions.clone(); - Box::new(move |info| { - handle_panic(info, &send_client_instructions); - }) - }); - - let stdin_ansi_parser = Arc::new(Mutex::new(StdinAnsiParser::new())); - let _stdin_thread = thread::Builder::new() - .name("stdin_handler".to_string()) - .spawn({ - let os_input = os_input.clone(); - let send_input_instructions = send_input_instructions.clone(); - move || stdin_loop(os_input, send_input_instructions, stdin_ansi_parser) - }); - - let clients: Vec; - os_input.send_to_server(ClientToServerMsg::ListClients); - #[allow(clippy::collapsible_match)] - loop { - if let Some((msg, _)) = os_input.recv_from_server() { - if let ServerToClientMsg::ActiveClients(active_clients) = msg { - clients = active_clients; - break; - } - } - } - debug!("The connected client id's are: {:?}.", clients); - - let _input_thread = thread::Builder::new() - .name("input_handler".to_string()) - .spawn({ - let send_client_instructions = send_client_instructions.clone(); - let command_is_executing = command_is_executing.clone(); - let os_input = os_input.clone(); - let default_mode = config_options.default_mode.unwrap_or_default(); - let session_name = session_name.to_string(); - move || { - input_actions( - os_input, - config, - config_options, - command_is_executing, - clients, - send_client_instructions, - default_mode, - receive_input_instructions, - actions, - session_name, - ) - } - }); - - let router_thread = thread::Builder::new() - .name("router".to_string()) - .spawn({ - let os_input = os_input.clone(); - let mut should_break = false; - move || loop { - if let Some((instruction, err_ctx)) = os_input.recv_from_server() { - err_ctx.update_thread_ctx(); - if let ServerToClientMsg::Exit(_) = instruction { - should_break = true; - } - send_client_instructions.send(instruction.into()).unwrap(); - if should_break { - break; - } - } - } - }) - .unwrap(); - - loop { - let (client_instruction, mut err_ctx) = receive_client_instructions - .recv() - .expect("failed to receive app instruction on channel"); - - err_ctx.add_call(ContextType::Client((&client_instruction).into())); - match client_instruction { - ClientInstruction::Exit(_) => { - os_input.send_to_server(ClientToServerMsg::ClientExited); - break; - }, - ClientInstruction::Error(_) => { - let _ = os_input.send_to_server(ClientToServerMsg::Action(Action::Quit, None)); - // handle_error(backtrace); - }, - ClientInstruction::Render(_) => { - // This is a fake client, that doesn't render, but - // dispatches actions. - }, - ClientInstruction::UnblockInputThread => { - command_is_executing.unblock_input_thread(); - }, - ClientInstruction::SwitchToMode(input_mode) => { - send_input_instructions - .send(InputInstruction::SwitchToMode(input_mode)) - .unwrap(); - }, - _ => {}, - } - } - router_thread.join().unwrap(); + unimplemented!() +// debug!("Starting fake Zellij client!"); +// let session_name = info.get_session_name(); +// +// // TODO: Ideally the `fake_client` would not need to specify these options, +// // but the `[NewTab:]` action depends on this state being +// // even in this client. +// let palette = config.themes.clone().map_or_else( +// || os_input.load_palette(), +// |t| { +// t.theme_config(&config_options) +// .unwrap_or_else(|| os_input.load_palette()) +// }, +// ); +// +// let full_screen_ws = os_input.get_terminal_size_using_fd(0); +// let client_attributes = ClientAttributes { +// size: full_screen_ws, +// style: Style { +// colors: palette, +// rounded_corners: config.ui.unwrap_or_default().pane_frames.rounded_corners, +// }, +// }; +// +// let first_msg = ClientToServerMsg::AttachClient(client_attributes, config_options.clone()); +// +// let zellij_ipc_pipe: PathBuf = { +// let mut sock_dir = zellij_utils::consts::ZELLIJ_SOCK_DIR.clone(); +// fs::create_dir_all(&sock_dir).unwrap(); +// zellij_utils::shared::set_permissions(&sock_dir, 0o700).unwrap(); +// sock_dir.push(session_name); +// sock_dir +// }; +// os_input.connect_to_server(&*zellij_ipc_pipe); +// os_input.send_to_server(first_msg); +// +// let mut command_is_executing = CommandIsExecuting::new(); +// +// let (send_client_instructions, receive_client_instructions): ChannelWithContext< +// ClientInstruction, +// > = channels::bounded(50); +// let send_client_instructions = SenderWithContext::new(send_client_instructions); +// +// let (send_input_instructions, receive_input_instructions): ChannelWithContext< +// InputInstruction, +// > = channels::bounded(50); +// let send_input_instructions = SenderWithContext::new(send_input_instructions); +// +// std::panic::set_hook({ +// use zellij_utils::errors::handle_panic; +// let send_client_instructions = send_client_instructions.clone(); +// Box::new(move |info| { +// handle_panic(info, &send_client_instructions); +// }) +// }); +// +// let stdin_ansi_parser = Arc::new(Mutex::new(StdinAnsiParser::new())); +// let _stdin_thread = thread::Builder::new() +// .name("stdin_handler".to_string()) +// .spawn({ +// let os_input = os_input.clone(); +// let send_input_instructions = send_input_instructions.clone(); +// move || stdin_loop(os_input, send_input_instructions, stdin_ansi_parser) +// }); +// +// let clients: Vec; +// os_input.send_to_server(ClientToServerMsg::ListClients); +// #[allow(clippy::collapsible_match)] +// loop { +// if let Some((msg, _)) = os_input.recv_from_server() { +// if let ServerToClientMsg::ActiveClients(active_clients) = msg { +// clients = active_clients; +// break; +// } +// } +// } +// debug!("The connected client id's are: {:?}.", clients); +// +// let _input_thread = thread::Builder::new() +// .name("input_handler".to_string()) +// .spawn({ +// let send_client_instructions = send_client_instructions.clone(); +// let command_is_executing = command_is_executing.clone(); +// let os_input = os_input.clone(); +// let default_mode = config_options.default_mode.unwrap_or_default(); +// let session_name = session_name.to_string(); +// move || { +// input_actions( +// os_input, +// config, +// config_options, +// command_is_executing, +// clients, +// send_client_instructions, +// default_mode, +// receive_input_instructions, +// actions, +// session_name, +// ) +// } +// }); +// +// let router_thread = thread::Builder::new() +// .name("router".to_string()) +// .spawn({ +// let os_input = os_input.clone(); +// let mut should_break = false; +// move || loop { +// if let Some((instruction, err_ctx)) = os_input.recv_from_server() { +// err_ctx.update_thread_ctx(); +// if let ServerToClientMsg::Exit(_) = instruction { +// should_break = true; +// } +// send_client_instructions.send(instruction.into()).unwrap(); +// if should_break { +// break; +// } +// } +// } +// }) +// .unwrap(); +// +// loop { +// let (client_instruction, mut err_ctx) = receive_client_instructions +// .recv() +// .expect("failed to receive app instruction on channel"); +// +// err_ctx.add_call(ContextType::Client((&client_instruction).into())); +// match client_instruction { +// ClientInstruction::Exit(_) => { +// os_input.send_to_server(ClientToServerMsg::ClientExited); +// break; +// }, +// ClientInstruction::Error(_) => { +// let _ = os_input.send_to_server(ClientToServerMsg::Action(Action::Quit, None)); +// // handle_error(backtrace); +// }, +// ClientInstruction::Render(_) => { +// // This is a fake client, that doesn't render, but +// // dispatches actions. +// }, +// ClientInstruction::UnblockInputThread => { +// command_is_executing.unblock_input_thread(); +// }, +// ClientInstruction::SwitchToMode(input_mode) => { +// send_input_instructions +// .send(InputInstruction::SwitchToMode(input_mode)) +// .unwrap(); +// }, +// _ => {}, +// } +// } +// router_thread.join().unwrap(); } diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 3b81694b41..da521edab5 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -144,13 +144,16 @@ pub fn start_client( envs::set_zellij("0".to_string()); config.env.set_vars(); - let palette = config.themes.clone().map_or_else( - || os_input.load_palette(), - |t| { - t.theme_config(&config_options) - .unwrap_or_else(|| os_input.load_palette()) - }, - ); +// let palette = config.themes.clone().map_or_else( +// || os_input.load_palette(), +// |t| { +// t.theme_config(&config_options) +// .unwrap_or_else(|| os_input.load_palette()) +// }, +// ); + let palette = config + .theme_config(&config_options) + .unwrap_or_else(|| os_input.load_palette()); let full_screen_ws = os_input.get_terminal_size_using_fd(0); let client_attributes = ClientAttributes { diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 28f309d338..97d4d8ac64 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -36,6 +36,8 @@ unicode-width = "0.1.8" miette = { version = "3.3.0", features = ["fancy"] } regex = "1.5.5" tempfile = "3.2.0" +knuffel = "2.0.0" +kdl = "4.3.0" #[cfg(not(target_family = "wasm"))] [target.'cfg(not(target_family = "wasm"))'.dependencies] diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index ed7accd326..fdf336d16c 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -1,221 +1,229 @@ -keybinds clear-defaults=true { +keybinds { normal { - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl n" { SwitchToMode "Resize" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Ctrl s" { SwitchToMode "Scroll" } - bind "Ctrl o" { SwitchToMode "Session" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl b" { SwitchToMode "Tmux" } - bind "Ctrl q" { Quit } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt =" "Alt +" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "Alt y" { + Run "diskonaut" { + cwd "/home/aram" + } + } // uncomment this and adjust key if using copy_on_select=false - // bind "Alt c" { Copy } + // bind "Alt c" { Copy; } } locked { - bind "Ctrl g" { SwitchToMode "Normal" } + bind "Ctrl g" { SwitchToMode "Normal"; } } resize { - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal" } - bind "Ctrl s" { SwitchToMode "Scroll" } - bind "Ctrl o" { SwitchToMode "Session" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl b" { SwitchToMode "Tmux" } - bind "Ctrl q" { Quit } - bind "h" "Left" { Resize "Left" } - bind "j" "Down" { Resize "Down" } - bind "k" "Up" { Resize "Up" } - bind "l" "Right" { Resize "Right" } - bind "=" "+" { Resize "Increase" } - bind "-" { Resize "Decrease" } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } } pane { - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl n" { SwitchToMode "Resize" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal" } - bind "Ctrl s" { SwitchToMode "Scroll" } - bind "Ctrl o" { SwitchToMode "Session" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl b" { SwitchToMode "Tmux" } - bind "Ctrl q" { Quit } - bind "h" "Left" { MoveFocus "Left" } - bind "l" "Right" { MoveFocus "Right" } - bind "j" "Down" { MoveFocus "Down" } - bind "k" "Up" { MoveFocus "Up" } - bind "p" { SwitchFocus } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } bind "n" { NewPane; SwitchToMode "Normal"; } bind "d" { NewPane "Down"; SwitchToMode "Normal"; } - bind "r" { NewPane "Right; SwitchToMode "Normal"; } + bind "r" { NewPane "Right"; SwitchToMode "Normal"; } bind "x" { CloseFocus; SwitchToMode "Normal"; } bind "f" { ToggleFocusFullscreen; SwitchToMode "Normal"; } - bind "z" { TogglePaneFrames; SwitchToMode "Normal" } - bind "w" { ToggleFloatingPanes; SwitchToMode "Normal" } - bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal" } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } - bind "c" { SwitchToMode "RenamePane" } + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "Normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; } } move { - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Ctrl n" { SwitchToMode "Resize" } - bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal" } - bind "Ctrl s" { SwitchToMode "Scroll" } - bind "Ctrl o" { SwitchToMode "Session" } - bind "Ctrl q" { Quit } - bind "n" "Tab" { MovePane } - bind "h" "Left" { MovePane "Left" } - bind "j" "Down" { MovePane "Down" } - bind "k" "Up" { MovePane "Up" } - bind "l" "Right" { MovePane "Right" } - bind "Alt n" { NewPane } - bind "Alt h" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } } tab { - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl n" { SwitchToMode "Resize" } - bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal" } - bind "Ctrl s" { SwitchToMode "Scroll" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl b" { SwitchToMode "Tmux" } - bind "Ctrl o" { SwitchToMode "Session" } - bind "Char r" { SwitchToMode "RenameTab" } - bind "Ctrl q" { Quit } - bind "h" "Left" "Up" "k" { GoToPreviousTab } - bind "l" "Right" "Down" "j" { GoToNextTab } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } bind "n" { NewTab; SwitchToMode "Normal"; } bind "x" { CloseTab; SwitchToMode "Normal"; } bind "s" { ToggleActiveSyncTab; SwitchToMode "Normal"; } - 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 "Tab" { ToggleTab } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } } scroll { - bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl b" { SwitchToMode "Tmux" } - bind "Ctrl o" { SwitchToMode "Session" } - bind "Ctrl n" { SwitchToMode "Resize" } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl n" { SwitchToMode "Resize"; } bind "e" { EditScrollback; SwitchToMode "Normal"; } bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } - bind "Ctrl q" { Quit } - bind "j" "Down" { ScrollDown } - bind "k" "Up" { ScrollUp } - bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown } - bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp } - bind "d" { HalfPageScrollDown } - bind "u" { HalfPageScrollUp } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } // uncomment this and adjust key if using copy_on_select=false - // bind "Alt c" { Copy } + // bind "Alt c" { Copy; } } - renametab: - bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal" } + renametab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt =" {Resize "Increase" } - bind "Alt +" {Resize "Increase" } - bind "Alt -" {Resize "Decrease" } - renamepane: - bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal" } - bind "Esc" { UndoRenamePane; SwitchToMode "Pane" } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } - session: - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl n" { SwitchToMode "Resize" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl b" { SwitchToMode "Tmux" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal" } - bind "Ctrl s" { SwitchToMode "Scroll" } - bind "Ctrl q" { Quit } - bind "d" { Detach } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "Alt +" "Alt =" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" {Resize "Increase"; } + bind "Alt +" {Resize "Increase"; } + bind "Alt -" {Resize "Decrease"; } + } + renamepane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "Pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + session { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } tmux { - bind "Ctrl g" { SwitchToMode "Locked" } - bind "Ctrl n" { SwitchToMode "Resize" } - bind "Ctrl p" { SwitchToMode "Pane" } - bind "Ctrl h" { SwitchToMode "Move" } - bind "Ctrl t" { SwitchToMode "Tab" } - bind "Enter" "Space" "Esc" { SwitchToMode Normal } - bind "[" { SwitchToMode "Scroll" } - bind "Ctrl q" { Quit } - bind "Ctrl b" { Write "Ctrl b"; SwitchToMode "Normal"; } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "[" { SwitchToMode "Scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "Normal"; } bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } bind "%" { NewPane "Right"; SwitchToMode "Normal"; } bind "z" { ToggleFocusFullscreen; SwitchToMode "Normal"; } bind "c" { NewTab; SwitchToMode "Normal"; } - bind "," { SwitchToMode "RenameTab" } + bind "," { SwitchToMode "RenameTab"; } bind "p" { GoToPreviousTab; SwitchToMode "Normal"; } bind "n" { GoToNextTab; SwitchToMode "Normal"; } bind "Left" { MoveFocus "Left"; SwitchToMode "Normal"; } @@ -226,15 +234,141 @@ keybinds clear-defaults=true { bind "l" { MoveFocus "Right"; SwitchToMode "Normal"; } bind "j" { MoveFocus "Down"; SwitchToMode "Normal"; } bind "k" { MoveFocus "Up"; SwitchToMode "Normal"; } - bind "Alt n" { NewPane } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left" } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right" } - bind "Alt j" "Alt Down" { MoveFocus "Down" } - bind "Alt k" "Alt Up" { MoveFocus "Up" } - bind "o" { FocusNextPane } - bind "Alt =" { Resize "Increase" } - bind "Alt +" { Resize "Increase" } - bind "Alt -" { Resize "Decrease" } - bind "d" { Detach } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// pane_frames true + +// Choose the theme that is specified in the themes section. +// For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +// Default: default +// theme "default" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +themes { + dracula { + // From https://github.com/dracula/zellij + 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 + fg 248 248 242 + cyan 139 233 253 + black 0 0 0 + white 255 255 255 } + nord { + fg "#D8DEE9" + bg "#2E3440" + black "#3B4252" + red "#BF616A" + green "#A3BE8C" + yellow "#EBCB8B" + blue "#81A1C1" + magenta "#B48EAD" + cyan "#88C0D0" + white "#E5E9F0" + orange "#D08770" + } +} + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + +ui { + pane_frames { + rounded_corners false + } +} + +env { + RUST_BACKTRACE 1 + foo "bar" } diff --git a/zellij-utils/assets/config/test_config_deleteme.kdl b/zellij-utils/assets/config/test_config_deleteme.kdl new file mode 100644 index 0000000000..7b8c8d86b3 --- /dev/null +++ b/zellij-utils/assets/config/test_config_deleteme.kdl @@ -0,0 +1,376 @@ +keybinds { + unbind "Ctrl g"; + normal clear-defaults=true { + // unbind "Alt ="; // TODO: removeme + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "Alt y" { + Run "diskonaut" { + cwd "/home/aram" + } + } + // uncomment this and adjust key if using copy_on_select=false + // bind "Alt c" { Copy; } + } + locked { + bind "Ctrl g" { SwitchToMode "Normal"; } + } + resize { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + pane { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "Normal"; } + bind "d" { NewPane "Down"; SwitchToMode "Normal"; } + bind "r" { NewPane "Right"; SwitchToMode "Normal"; } + bind "x" { CloseFocus; SwitchToMode "Normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "Normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "Normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; } + } + move { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tab { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "Normal"; } + bind "x" { CloseTab; SwitchToMode "Normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "Normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "e" { EditScrollback; SwitchToMode "Normal"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + // uncomment this and adjust key if using copy_on_select=false + // bind "Alt c" { Copy; } + } + renametab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" {Resize "Increase"; } + bind "Alt +" {Resize "Increase"; } + bind "Alt -" {Resize "Decrease"; } + } + renamepane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "Pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + session { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "[" { SwitchToMode "Scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "Normal"; } + bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } + bind "%" { NewPane "Right"; SwitchToMode "Normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "Normal"; } + bind "c" { NewTab; SwitchToMode "Normal"; } + bind "," { SwitchToMode "RenameTab"; } + bind "p" { GoToPreviousTab; SwitchToMode "Normal"; } + bind "n" { GoToNextTab; SwitchToMode "Normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "Normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "Normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "Normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "Normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "Normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "Normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "Normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "Normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// pane_frames true + +// Choose the theme that is specified in the themes section. +// For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +// Default: default +// theme "default" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +themes { + dracula { + // From https://github.com/dracula/zellij + 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 + fg 248 248 242 + cyan 139 233 253 + black 0 0 0 + white 255 255 255 + } + nord { + fg "#D8DEE9" + bg "#2E3440" + black "#3B4252" + red "#BF616A" + green "#A3BE8C" + yellow "#EBCB8B" + blue "#81A1C1" + magenta "#B48EAD" + cyan "#88C0D0" + white "#E5E9F0" + orange "#D08770" + } +} + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + +ui { + pane_frames { + rounded_corners false + } +} + +env { + RUST_BACKTRACE 1 + foo "bar" +} diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index c50ef47902..a8060bc979 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -140,6 +140,27 @@ pub enum InputMode { Tmux, } +impl TryFrom<&str> for InputMode { + type Error = String; + fn try_from(mode: &str) -> Result { + match mode { + "normal" | "Normal" => Ok(InputMode::Normal), + "locked" | "Locked" => Ok(InputMode::Locked), + "resize" | "Resize" => Ok(InputMode::Resize), + "pane" | "Pane" => Ok(InputMode::Pane), + "tab" | "Tab" => Ok(InputMode::Tab), + "scroll" | "Scroll" => Ok(InputMode::Scroll), + "renametab" | "RenameTab" => Ok(InputMode::RenameTab), + "renamepane" | "RenamePane" => Ok(InputMode::RenamePane), + "session" | "Session" => Ok(InputMode::Session), + "move" | "Move" => Ok(InputMode::Move), + "prompt" | "Prompt" => Ok(InputMode::Prompt), + "tmux" | "Tmux" => Ok(InputMode::Tmux), + _ => Err(format!("Unrecognized mode: {}", mode)), + } + } +} + impl Default for InputMode { fn default() -> InputMode { InputMode::Normal diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index 9e457b030d..80a34a4aba 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -35,17 +35,22 @@ pub fn get_socket_dir() -> Result { /// Manage ENVIRONMENT VARIABLES from the configuration and the layout files #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] -pub struct EnvironmentVariablesFromYaml { +pub struct EnvironmentVariables { env: HashMap, } -impl EnvironmentVariablesFromYaml { +impl EnvironmentVariables { /// Merges two structs, keys from `other` supersede keys from `self` pub fn merge(&self, other: Self) -> Self { let mut env = self.clone(); env.env.extend(other.env); env } + pub fn from_data(data: HashMap) -> Self { + EnvironmentVariables { + env: data + } + } /// Set all the ENVIRONMENT VARIABLES, that are configured /// in the configuration and layout files diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 0550792dfd..1f236a944a 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -6,6 +6,8 @@ use crate::data::InputMode; use crate::input::options::OnForceClose; use serde::{Deserialize, Serialize}; +use std::str::FromStr; + use crate::position::Position; /// The four directions (left, right, up, down). @@ -16,6 +18,18 @@ pub enum Direction { Up, Down, } +impl FromStr for Direction { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "Left" => Ok(Direction::Left), + "Right" => Ok(Direction::Right), + "Up" => Ok(Direction::Up), + "Down" => Ok(Direction::Down), + _ => Err(format!("Failed to parse Direction. Unknown Direction: {}", s)), + } + } +} #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum ResizeDirection { @@ -26,6 +40,20 @@ pub enum ResizeDirection { Increase, Decrease, } +impl FromStr for ResizeDirection { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "Left" => Ok(ResizeDirection::Left), + "Right" => Ok(ResizeDirection::Right), + "Up" => Ok(ResizeDirection::Up), + "Down" => Ok(ResizeDirection::Down), + "Increase" => Ok(ResizeDirection::Increase), + "Decrease" => Ok(ResizeDirection::Decrease), + _ => Err(format!("Failed to parse ResizeDirection. Unknown ResizeDirection: {}", s)), + } + } +} // As these actions are bound to the default config, please // do take care when refactoring - or renaming. diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 4b9f4100b0..927a45ff6d 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -3,20 +3,32 @@ use std::fmt; use std::fs::File; use std::io::{self, Read}; use std::path::{Path, PathBuf}; +use std::str::FromStr; use thiserror::Error; +use crate::data::{self, Palette, PaletteColor, PluginTag, CharOrArrow}; +use super::layout::RunPluginLocation; + +use crate::input::{InputMode, Key}; +use super::actions::{Action, Direction}; + +use std::collections::{HashMap, HashSet}; + +use kdl::{KdlDocument, KdlValue, KdlNode}; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; use super::keybinds::{Keybinds, KeybindsFromYaml}; -use super::options::Options; -use super::plugins::{PluginsConfig, PluginsConfigError, PluginsConfigFromYaml}; -use super::theme::{ThemesFromYaml, UiConfigFromYaml}; +use super::options::{Options, OnForceClose, Clipboard}; +use super::plugins::{PluginsConfig, PluginsConfigError, PluginsConfigFromYaml, PluginConfig, PluginType}; +use super::theme::{ThemesFromYaml, UiConfig, Theme, FrameConfig}; use crate::cli::{CliArgs, Command}; -use crate::envs::EnvironmentVariablesFromYaml; +use crate::envs::EnvironmentVariables; use crate::setup; +use crate::{entry_count, kdl_entries_as_i64, kdl_first_entry_as_string, kdl_first_entry_as_i64}; -const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml"; +// const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml"; +const DEFAULT_CONFIG_FILE_NAME: &str = "config.kdl"; type ConfigResult = Result; @@ -28,25 +40,40 @@ pub struct ConfigFromYaml { pub keybinds: Option, pub themes: Option, #[serde(flatten)] - pub env: Option, + pub env: Option, #[serde(default)] pub plugins: PluginsConfigFromYaml, - pub ui: Option, + pub ui: Option, } /// Main configuration. -#[derive(Debug, Clone, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize, knuffel::Decode)] pub struct Config { - pub keybinds: Keybinds, + pub keybinds: HashMap>>, // TODO: make this a type pub options: Options, - pub themes: Option, + pub themes: Option>, // TODO: typify? pub plugins: PluginsConfig, - pub ui: Option, - pub env: EnvironmentVariablesFromYaml, + // pub ui: Option, + pub ui: Option, + pub env: EnvironmentVariables, } +// #[derive(Debug, Clone, PartialEq, Deserialize)] +// pub struct Config { +// pub keybinds: Keybinds, +// pub options: Options, +// pub themes: Option, +// pub plugins: PluginsConfig, +// pub ui: Option, +// pub env: EnvironmentVariablesFromYaml, +// } #[derive(Error, Debug)] pub enum ConfigError { + // Deserialization error + #[error("Deserialization error: {0}")] + KdlDeserializationError(#[from] kdl::KdlError), + #[error("KdlDeserialization error: {0}")] + KdlParsingError(String), // Deserialization error #[error("Deserialization error: {0}")] Serde(#[from] serde_yaml::Error), @@ -69,10 +96,11 @@ pub enum ConfigError { impl Default for Config { fn default() -> Self { - let keybinds = Keybinds::default(); + // let keybinds = Keybinds::default(); + let keybinds = HashMap::new(); let options = Options::default(); let themes = None; - let env = EnvironmentVariablesFromYaml::default(); + let env = EnvironmentVariables::default(); let plugins = PluginsConfig::default(); let ui = None; @@ -92,7 +120,9 @@ impl TryFrom<&CliArgs> for Config { fn try_from(opts: &CliArgs) -> ConfigResult { if let Some(ref path) = opts.config { - return Config::new(path); + let default_config = Config::from_default_assets()?; + return Config::from_path(path, Some(default_config)); + // return Config::new(path); } if let Some(Command::Setup(ref setup)) = opts.command { @@ -109,7 +139,9 @@ impl TryFrom<&CliArgs> for Config { if let Some(ref config) = config_dir { let path = config.join(DEFAULT_CONFIG_FILE_NAME); if path.exists() { - Config::new(&path) + let default_config = Config::from_default_assets()?; + Config::from_path(&path, Some(default_config)) + // Config::new(&path) } else { Config::from_default_assets() } @@ -120,46 +152,337 @@ impl TryFrom<&CliArgs> for Config { } impl Config { + pub fn theme_config(&self, opts: &Options) -> Option { + match &opts.theme { + // Some(theme) => from_yaml.from_default_theme(theme.to_owned()), + Some(theme_name) => self.themes.as_ref() + .and_then(|themes| themes.get(theme_name)) + .map(|theme| theme.palette), + // None => from_yaml.from_default_theme("default".into()), + None => self.themes.as_ref() + .and_then(|themes| themes.get("default")) + .map(|theme| theme.palette) + } + } /// Uses defaults, but lets config override them. - pub fn from_yaml(yaml_config: &str) -> ConfigResult { - let maybe_config_from_yaml: Option = match serde_yaml::from_str(yaml_config) - { - Err(e) => { - // needs direct check, as `[ErrorImpl]` is private - // https://github.com/dtolnay/serde-yaml/issues/121 - if yaml_config.is_empty() { - return Ok(Config::default()); - } - return Err(ConfigError::Serde(e)); - }, - Ok(config) => config, +// pub fn from_yaml(yaml_config: &str) -> ConfigResult { +// let maybe_config_from_yaml: Option = match serde_yaml::from_str(yaml_config) +// { +// Err(e) => { +// // needs direct check, as `[ErrorImpl]` is private +// // https://github.com/dtolnay/serde-yaml/issues/121 +// if yaml_config.is_empty() { +// return Ok(Config::default()); +// } +// return Err(ConfigError::Serde(e)); +// }, +// Ok(config) => config, +// }; +// +// match maybe_config_from_yaml { +// None => Ok(Config::default()), +// Some(config) => config.try_into(), +// } +// } + /// Uses defaults, but lets config override them. + pub fn parse_keybindings(kdl_keybinds: &KdlNode, keybindings_to_override: Option>>>) -> Result>>, String> { + let clear_defaults = kdl_keybinds.get("clear-defaults").and_then(|c| c.value().as_bool()).unwrap_or(false) == true; + let mut keybinds_from_config: HashMap>> = if clear_defaults {HashMap::new()} else {keybindings_to_override.unwrap_or_else(|| HashMap::new())}; + for mode in kdl_keybinds.children().unwrap().nodes() { + let mode_name = mode.name().value(); + if mode_name == "unbind" { + continue; + } + let input_mode = InputMode::try_from(mode_name).unwrap(); + let input_mode_keybinds = keybinds_from_config.entry(input_mode).or_insert_with(HashMap::new); + let clear_defaults_for_mode = mode.get("clear-defaults").is_some(); + if clear_defaults_for_mode { + input_mode_keybinds.clear(); + } + for key_block in mode.children().unwrap().nodes() { + let key_block_name = key_block.name().value(); + if key_block_name == "bind" { + let keys: Vec = key_block.entries().iter().map(|key_shortcut| { + let key_shortcut = key_shortcut.value(); + Key::try_from(key_shortcut).unwrap() + }).collect(); + let actions: Vec = key_block.children().unwrap().nodes().iter().map(|action| { + let action_name = action.name().value(); + let action_arguments: Vec<&KdlValue> = action.entries().iter().map(|arg| arg.value()).collect(); + let action_children: Vec<&KdlDocument> = action.children().iter().copied().collect(); + Action::try_from((action_name, action_arguments, action_children)).unwrap() + }).collect(); + for key in keys { + input_mode_keybinds.insert(key, actions.clone()); + } + } + } + for key_block in mode.children().unwrap().nodes() { + // we loop twice so that the unbinds always happen after the binds + let key_block_name = key_block.name().value(); + if key_block_name == "unbind" { + let keys: Vec = key_block.entries().iter().map(|key_shortcut| { + let key_shortcut = key_shortcut.value(); + Key::try_from(key_shortcut).unwrap() + }).collect(); + for key in keys { + input_mode_keybinds.remove(&key); + } + } + } + // keybinds_from_config.insert(input_mode, input_mode_keybinds); + } + if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { + let keys: Vec = global_unbind.entries().iter().map(|key_shortcut| { + let key_shortcut = key_shortcut.value(); + Key::try_from(key_shortcut).unwrap() + }).collect(); + for mode in keybinds_from_config.values_mut() { + for key in &keys { + mode.remove(&key); + } + } }; - - match maybe_config_from_yaml { - None => Ok(Config::default()), - Some(config) => config.try_into(), + Ok(keybinds_from_config) + } + fn parse_options(kdl_config: &KdlDocument) -> Options { + // parse options + let on_force_close = kdl_config.get("on_force_close") + .and_then(|on_force_close| on_force_close.entries().iter().next()) + .and_then(|on_force_close| on_force_close.value().as_string()) + .and_then(|on_force_close| OnForceClose::from_str(on_force_close).ok()); + let simplified_ui = kdl_config.get("simplified_ui") + .and_then(|simplified_ui| simplified_ui.entries().iter().next()) + .and_then(|simplified_ui| simplified_ui.value().as_bool()); + let default_shell = kdl_config.get("default_shell") + .and_then(|default_shell| default_shell.entries().iter().next()) + .and_then(|default_shell| default_shell.value().as_string()) + .map(|default_shell| PathBuf::from(default_shell)); + let pane_frames = kdl_config.get("pane_frames") + .and_then(|pane_frames| pane_frames.entries().iter().next()) + .and_then(|pane_frames| pane_frames.value().as_bool()); + let theme = kdl_config.get("theme") + .and_then(|theme| theme.entries().iter().next()) + .and_then(|theme| theme.value().as_string()) + .map(|theme| theme.to_string()); + let default_mode = kdl_config.get("default_mode") + .and_then(|default_mode| default_mode.entries().iter().next()) + .and_then(|default_mode| default_mode.value().as_string()) + .and_then(|default_mode| InputMode::try_from(default_mode).ok()); + let mouse_mode = kdl_config.get("mouse_mode") + .and_then(|mouse_mode| mouse_mode.entries().iter().next()) + .and_then(|mouse_mode| mouse_mode.value().as_bool()); + let scroll_buffer_size = kdl_config.get("scroll_buffer_size") + .and_then(|scroll_buffer_size| scroll_buffer_size.entries().iter().next()) + .and_then(|scroll_buffer_size| scroll_buffer_size.value().as_i64()) + .map(|scroll_buffer_size| scroll_buffer_size as usize); + let copy_command = kdl_config.get("copy_command") + .and_then(|copy_command| copy_command.entries().iter().next()) + .and_then(|copy_command| copy_command.value().as_string()) + .map(|copy_command| copy_command.to_string()); + let copy_clipboard = kdl_config.get("copy_clipboard") + .and_then(|copy_clipboard| copy_clipboard.entries().iter().next()) + .and_then(|copy_clipboard| copy_clipboard.value().as_string()) + .and_then(|copy_clipboard| Clipboard::from_str(copy_clipboard).ok()); + let copy_on_select = kdl_config.get("copy_on_select") + .and_then(|copy_on_select| copy_on_select.entries().iter().next()) + .and_then(|copy_on_select| copy_on_select.value().as_bool()); + let scrollback_editor = kdl_config.get("scrollback_editor") + .and_then(|scrollback_editor| scrollback_editor.entries().iter().next()) + .and_then(|scrollback_editor| scrollback_editor.value().as_string()) + .map(|scrollback_editor| PathBuf::from(scrollback_editor)); + let mirror_session = kdl_config.get("mirror_session") + .and_then(|mirror_session| mirror_session.entries().iter().next()) + .and_then(|mirror_session| mirror_session.value().as_bool()); + Options { + simplified_ui, + theme, + default_mode, + default_shell, + default_layout: Some(PathBuf::from("default")), // TODO + layout_dir: None, // TODO + mouse_mode, + pane_frames, + mirror_session, + on_force_close, + scroll_buffer_size, + copy_command, + copy_clipboard, + copy_on_select, + scrollback_editor, + } + } + pub fn parse_themes(themes_from_kdl: &KdlNode) -> Result, String> { + let mut themes: HashMap = HashMap::new(); + for theme_config in themes_from_kdl.children().unwrap().nodes() { // TODO: no unwraps here or anywhere + let theme_name = theme_config.name().value(); + let theme_colors = theme_config.children().unwrap(); + let theme = Theme { + palette: Palette { + fg: PaletteColor::try_from(("fg", theme_colors))?, + bg: PaletteColor::try_from(("bg", theme_colors))?, + red: PaletteColor::try_from(("red", theme_colors))?, + green: PaletteColor::try_from(("green", theme_colors))?, + yellow: PaletteColor::try_from(("yellow", theme_colors))?, + blue: PaletteColor::try_from(("blue", theme_colors))?, + magenta: PaletteColor::try_from(("magenta", theme_colors))?, + orange: PaletteColor::try_from(("orange", theme_colors))?, + cyan: PaletteColor::try_from(("cyan", theme_colors))?, + black: PaletteColor::try_from(("black", theme_colors))?, + white: PaletteColor::try_from(("white", theme_colors))?, + ..Default::default() + } + }; + themes.insert(theme_name.into(), theme); } + Ok(themes) } + pub fn parse_plugins(kdl_plugin_config: &KdlNode) -> Result, String> { + let mut plugins: HashMap = HashMap::new(); + for plugin_config in kdl_plugin_config.children().unwrap().nodes() { // TODO: no unwraps here or anywhere + let plugin_name = String::from(plugin_config.name().value()); + let plugin_tag = PluginTag::new(&plugin_name); + let path = plugin_config.children().unwrap() + .get("path") + .ok_or("Plugin path not found")? + .entries() + .iter() + .next() + .ok_or("Plugin path not found")? + .value() + .as_string() + .ok_or("Invalid plugin path")?; + let path = PathBuf::from(path); + // let allow_exec_host_cmd = plugin_config.children().unwrap().get("_allow_exec_host_cmd").and_then(|a| a.value().as_bool()).unwrap_or(false); + let allow_exec_host_cmd = plugin_config.children().unwrap() + .get("_allow_exec_host_cmd") + .and_then(|a| a.entries().iter().next()) + .and_then(|a| a.value().as_bool()) + .unwrap_or(false); + let plugin_config = PluginConfig { + path, + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(plugin_tag.clone()), + _allow_exec_host_cmd: allow_exec_host_cmd, + }; + plugins.insert(plugin_tag, plugin_config); + } + Ok(plugins) + } + pub fn parse_ui_config(kdl_ui_config: &KdlNode) -> Result { + let mut ui_config = UiConfig::default(); + if let Some(pane_frames) = kdl_ui_config.children().unwrap().get("pane_frames") { + let rounded_corners = pane_frames.children().unwrap().get("rounded_corners").unwrap().entries().iter().next().unwrap().value().as_bool().unwrap_or(false); + let frame_config = FrameConfig { rounded_corners }; + ui_config.pane_frames = frame_config; + } + Ok(ui_config) + } + pub fn parse_env_variables_config(kdl_env_variables: &KdlNode) -> Result, String> { + let mut env: HashMap = HashMap::new(); + for env_var in kdl_env_variables.children().unwrap().nodes() { // TODO: no unwraps here or anywhere + let env_var_name = String::from(env_var.name().value()); + let env_var_value = env_var + .entries() + .iter() + .next() + .ok_or("environment variable must have a value")?; + let env_var_str_value = env_var_value + .value() + .as_string() + .map(|s| s.to_string()); + let env_var_int_value = env_var_value + .value() + .as_i64() + .map(|s| format!("{}", s.to_string())); + let env_var_value = env_var_str_value + .or(env_var_int_value) + .ok_or(format!("Failed to parse env var value: {:?}", env_var_value))?; + env.insert(env_var_name, env_var_value); + } + Ok(env) + } + pub fn from_kdl(kdl_config: &str, base_config: Option) -> ConfigResult { + // TODO: CONTINUE HERE + // - adapt the existing tests to work with the new way + // - then write new tests + // - then refactor + // - then move on to layouts and everything else (what?) + let mut config = base_config.unwrap_or_else(|| Config::default()); + let kdl_config: KdlDocument = kdl_config.parse()?; - /// Deserializes from given path. - pub fn new(path: &Path) -> ConfigResult { - match File::open(path) { - Ok(mut file) => { - let mut yaml_config = String::new(); - file.read_to_string(&mut yaml_config) - .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; - Ok(Config::from_yaml(&yaml_config)?) - }, - Err(e) => Err(ConfigError::IoPath(e, path.into())), + if let Some(keybinds) = kdl_config.get("keybinds") { + let keybinds_from_config = Config::parse_keybindings(&keybinds, Some(config.keybinds)).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse keybindings: {:?}", e)))?; + config.keybinds = keybinds_from_config; } + + let options = Config::parse_options(&kdl_config); + config.options = options; + + if let Some(kdl_themes) = kdl_config.get("themes") { + let themes = Config::parse_themes(&kdl_themes).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse themes: {:?}", e)))?; + config.themes = Some(themes); + } + + if let Some(kdl_plugin_config) = kdl_config.get("plugins") { + let plugins = Config::parse_plugins(&kdl_plugin_config).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse plugins: {:?}", e)))?; + config.plugins = PluginsConfig::from_data(plugins); + } + + if let Some(kdl_ui_config) = kdl_config.get("ui") { + let ui_config = Config::parse_ui_config(&kdl_ui_config).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse ui config: {:?}", e)))?; + config.ui = Some(ui_config); + } + + if let Some(env_config) = kdl_config.get("env") { + let env = Config::parse_env_variables_config(&env_config).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse env variable config: {:?}", e)))?; + config.env = EnvironmentVariables::from_data(env); + } + + Ok(config) } + /// Deserializes from given path. +// pub fn new(path: &Path) -> ConfigResult { +// match File::open(path) { +// Ok(mut file) => { +// let mut kdl_config = String::new(); +// file.read_to_string(&mut kdl_config) +// .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; +// Ok(Config::from_kdl(&kdl_config)?) +// +// // let mut yaml_config = String::new(); +// // file.read_to_string(&mut yaml_config) +// // .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; +// // Ok(Config::from_yaml(&yaml_config)?) +// }, +// Err(e) => Err(ConfigError::IoPath(e, path.into())), +// } +// } + /// Gets default configuration from assets // TODO Deserialize the Config from bytes &[u8], // once serde-yaml supports zero-copy pub fn from_default_assets() -> ConfigResult { let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec())?; - Self::from_yaml(&cfg) + // Self::from_yaml(&cfg) + Self::from_kdl(&cfg, None) + } + pub fn from_path(path: &PathBuf, default_config: Option) -> ConfigResult { + match File::open(path) { + Ok(mut file) => { + let mut kdl_config = String::new(); + file.read_to_string(&mut kdl_config) + .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; + Config::from_kdl(&kdl_config, default_config) + +// let mut yaml_config = String::new(); +// file.read_to_string(&mut yaml_config) +// .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; +// Ok(Config::from_yaml(&yaml_config)?) + }, + Err(e) => Err(ConfigError::IoPath(e, path.into())), + } } /// Merges two Config structs into one Config struct @@ -177,26 +500,27 @@ impl Config { } } -impl TryFrom for Config { - type Error = ConfigError; - - fn try_from(config_from_yaml: ConfigFromYaml) -> ConfigResult { - let keybinds = Keybinds::get_default_keybinds_with_config(config_from_yaml.keybinds); - let options = Options::from_yaml(config_from_yaml.options); - let themes = config_from_yaml.themes; - let env = config_from_yaml.env.unwrap_or_default(); - let plugins = PluginsConfig::get_plugins_with_default(config_from_yaml.plugins.try_into()?); - let ui = config_from_yaml.ui; - Ok(Self { - keybinds, - options, - plugins, - themes, - env, - ui, - }) - } -} +// impl TryFrom for Config { +// type Error = ConfigError; +// +// fn try_from(config_from_yaml: ConfigFromYaml) -> ConfigResult { +// // let keybinds = Keybinds::get_default_keybinds_with_config(config_from_yaml.keybinds); +// let keybinds = HashMap::new(); +// let options = Options::from_yaml(config_from_yaml.options); +// let themes = config_from_yaml.themes; +// let env = config_from_yaml.env.unwrap_or_default(); +// let plugins = PluginsConfig::get_plugins_with_default(config_from_yaml.plugins.try_into()?); +// let ui = config_from_yaml.ui; +// Ok(Self { +// keybinds, +// options, +// plugins, +// themes, +// env, +// ui, +// }) +// } +// } // TODO: Split errors up into separate modules #[derive(Debug, Clone)] @@ -240,6 +564,7 @@ mod config_test { #[test] fn try_from_cli_args_with_config() { + // makes sure loading a config file with --config tries to load the config let arbitrary_config = PathBuf::from("nonexistent.yaml"); let opts = CliArgs { config: Some(arbitrary_config), @@ -252,6 +577,7 @@ mod config_test { #[test] fn try_from_cli_args_with_option_clean() { + // makes sure --clean works... TODO: how can this actually fail now? use crate::setup::Setup; let opts = CliArgs { command: Some(Command::Setup(Setup { @@ -283,13 +609,624 @@ mod config_test { let tmp = tempdir().unwrap(); opts.config_dir = Some(tmp.path().to_path_buf()); let result = Config::try_from(&opts); - assert_eq!(result.unwrap(), Config::default()); + assert_eq!(result.unwrap(), Config::from_default_assets().unwrap()); } #[test] fn try_from_cli_args_default() { let opts = CliArgs::default(); let result = Config::try_from(&opts); - assert_eq!(result.unwrap(), Config::default()); + assert_eq!(result.unwrap(), Config::from_default_assets().unwrap()); + } + + // TODO: CONTINUE HERE (04/08) - write these test cases, then refactor + + #[test] + fn can_define_keybindings_in_configfile() { + let config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let ctrl_g_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding successfully defined in config"); + } + + #[test] + fn can_define_multiple_keybinds_for_same_action() { + let config_contents = r#" + keybinds { + normal { + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let alt_h_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| + normal_mode_keybindings.get(&Key::Alt(CharOrArrow::Direction(data::Direction::Left))) + ); + let alt_left_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| + normal_mode_keybindings.get(&Key::Alt(CharOrArrow::Char('h'))) + ); + assert_eq!(alt_h_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "First keybinding successfully defined in config"); + assert_eq!(alt_left_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "Second keybinding successfully defined in config"); + } + + #[test] + fn can_define_series_of_actions_for_same_keybinding() { + let config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Action series successfully defined"); } + + #[test] + fn keybindings_bind_order_is_preserved() { + let config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Second keybinding was applied"); + } + + #[test] + fn uppercase_and_lowercase_keybindings_are_distinct() { + let config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "Z" { SwitchToMode "Resize"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + let uppercase_z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('Z')) + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Lowercase z successfully bound"); + assert_eq!(uppercase_z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Uppercase z successfully bound"); + } + + #[test] + fn can_override_keybindings() { + let default_config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds { + pane { + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from config overrode keybinding from default config"); + } + + #[test] + fn can_add_to_default_keybindings() { + // this test just makes sure keybindings defined in a custom config are added to different + // keybindings defined in the default config + let default_config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds { + pane { + bind "r" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + let r_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('r')) + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Keybinding from default config bound"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from custom config bound as well"); + } + + #[test] + fn can_clear_default_keybindings() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds clear-defaults=true { + normal { + bind "Ctrl r" { SwitchToMode "Locked"; } + } + pane { + bind "r" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + let ctrl_r_in_normal_mode = config.keybinds + .get(&InputMode::Normal) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Ctrl('r')) + ); + let r_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('r')) + ); + assert_eq!(ctrl_g_normal_mode_action, None, "Keybinding from normal mode in default config cleared"); + assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); + assert_eq!(ctrl_r_in_normal_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding from normal mode in custom config still bound"); + } + + #[test] + fn can_clear_default_keybindings_per_single_mode() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds { + pane clear-defaults=true { + bind "r" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + let r_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('r')) + ); + assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode from default config not cleared"); + assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); + } + + #[test] + fn can_unbind_multiple_keys_globally() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "r" { TogglePaneFrames; } + } + } + "#; + let config_contents = r#" + keybinds { + unbind "Ctrl g" "z" + pane { + bind "t" { SwitchToMode "Tab"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let ctrl_g_pane_mode_action = config.keybinds + .get(&InputMode::Pane) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let r_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('r')) + ); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + let t_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('t')) + ); + assert_eq!(ctrl_g_normal_mode_action, None, "First keybind uncleared in one mode"); + assert_eq!(ctrl_g_pane_mode_action, None, "First keybind uncleared in another mode"); + assert_eq!(z_in_pane_mode, None, "Second keybind cleared as well"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); + assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); + } + + #[test] + fn can_unbind_multiple_keys_per_single_mode() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "r" { TogglePaneFrames; } + } + } + "#; + let config_contents = r#" + keybinds { + pane { + unbind "Ctrl g" "z" + bind "t" { SwitchToMode "Tab"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config.keybinds + .get(&InputMode::Normal) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let ctrl_g_pane_mode_action = config.keybinds + .get(&InputMode::Pane) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let r_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('r')) + ); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + let t_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('t')) + ); + assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode not cleared"); + assert_eq!(ctrl_g_pane_mode_action, None, "First Keybind cleared in its mode"); + assert_eq!(z_in_pane_mode, None, "Second keybind cleared in its mode as well"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); + assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); + } + + #[test] + fn keybindings_unbinds_happen_after_binds() { + let config_contents = r#" + keybinds { + pane { + unbind "z" + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config.keybinds + .get(&InputMode::Pane) + .and_then(|pane_mode_keybindings| + pane_mode_keybindings.get(&Key::Char('z')) + ); + assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound"); + } + + + #[test] + fn can_define_options_in_configfile() { + // TODO: consider writing a macro to generate a test like this for each option + let config_contents = r#" + simplified_ui true + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let simplified_ui_in_config = config.options.simplified_ui; + assert_eq!(simplified_ui_in_config, Some(true), "Option set in config"); + } + + #[test] + fn can_define_themes_in_configfile() { + let config_contents = r#" + 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 + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let mut expected_themes = HashMap::new(); + expected_themes.insert("dracula".into(), Theme { + palette: Palette { + fg: PaletteColor::Rgb((248, 248, 242)), + bg: PaletteColor::Rgb((40, 42, 54)), + red: PaletteColor::Rgb((255, 85, 85)), + green: PaletteColor::Rgb((80, 250, 123)), + yellow: PaletteColor::Rgb((241, 250, 140)), + blue: PaletteColor::Rgb((98, 114, 164)), + magenta: PaletteColor::Rgb((255, 121, 198)), + orange: PaletteColor::Rgb((255, 184, 108)), + cyan: PaletteColor::Rgb((139, 233, 253)), + black: PaletteColor::Rgb((0, 0, 0)), + white: PaletteColor::Rgb((255, 255, 255)), + ..Default::default() + } + }); + assert_eq!(config.themes, Some(expected_themes), "Theme defined in config"); + } + + #[test] + fn can_define_multiple_themes_including_hex_themes_in_configfile() { + let config_contents = r##" + 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 "#D8DEE9" + bg "#2E3440" + black "#3B4252" + red "#BF616A" + green "#A3BE8C" + yellow "#EBCB8B" + blue "#81A1C1" + magenta "#B48EAD" + cyan "#88C0D0" + white "#E5E9F0" + orange "#D08770" + } + } + "##; + let config = Config::from_kdl(config_contents, None).unwrap(); + let mut expected_themes = HashMap::new(); + expected_themes.insert("dracula".into(), Theme { + palette: Palette { + fg: PaletteColor::Rgb((248, 248, 242)), + bg: PaletteColor::Rgb((40, 42, 54)), + red: PaletteColor::Rgb((255, 85, 85)), + green: PaletteColor::Rgb((80, 250, 123)), + yellow: PaletteColor::Rgb((241, 250, 140)), + blue: PaletteColor::Rgb((98, 114, 164)), + magenta: PaletteColor::Rgb((255, 121, 198)), + orange: PaletteColor::Rgb((255, 184, 108)), + cyan: PaletteColor::Rgb((139, 233, 253)), + black: PaletteColor::Rgb((0, 0, 0)), + white: PaletteColor::Rgb((255, 255, 255)), + ..Default::default() + } + }); + expected_themes.insert("nord".into(), Theme { + palette: Palette { + fg: PaletteColor::Rgb((216, 222, 233)), + bg: PaletteColor::Rgb((46, 52, 64)), + black: PaletteColor::Rgb((59, 66, 82)), + red: PaletteColor::Rgb((191, 97, 106)), + green: PaletteColor::Rgb((163, 190, 140)), + yellow: PaletteColor::Rgb((235, 203, 139)), + blue: PaletteColor::Rgb((129, 161, 193)), + magenta: PaletteColor::Rgb((180, 142, 173)), + cyan: PaletteColor::Rgb((136, 192, 208)), + white: PaletteColor::Rgb((229, 233, 240)), + orange: PaletteColor::Rgb((208, 135, 112)), + ..Default::default() + } + }); + assert_eq!(config.themes, Some(expected_themes), "Theme defined in config"); + } + + #[test] + fn can_define_eight_bit_themes() { + let config_contents = r#" + themes { + eight_bit_theme { + fg 248 + bg 40 + red 255 + green 80 + yellow 241 + blue 98 + magenta 255 + orange 255 + cyan 139 + black 1 + white 255 + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let mut expected_themes = HashMap::new(); + expected_themes.insert("eight_bit_theme".into(), Theme { + palette: Palette { + fg: PaletteColor::EightBit(248), + bg: PaletteColor::EightBit(40), + red: PaletteColor::EightBit(255), + green: PaletteColor::EightBit(80), + yellow: PaletteColor::EightBit(241), + blue: PaletteColor::EightBit(98), + magenta: PaletteColor::EightBit(255), + orange: PaletteColor::EightBit(255), + cyan: PaletteColor::EightBit(139), + black: PaletteColor::EightBit(1), + white: PaletteColor::EightBit(255), + ..Default::default() + } + }); + assert_eq!(config.themes, Some(expected_themes), "Theme defined in config"); + } + + #[test] + fn can_define_plugin_configuration_in_configfile() { + let config_contents = r#" + plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { + path "strider" + _allow_exec_host_cmd true + } + compact-bar { path "compact-bar"; } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let mut expected_plugin_configuration = HashMap::new(); + expected_plugin_configuration.insert(PluginTag::new("tab-bar"), PluginConfig { + path: PathBuf::from("tab-bar"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + _allow_exec_host_cmd: false + }); + expected_plugin_configuration.insert(PluginTag::new("status-bar"), PluginConfig { + path: PathBuf::from("status-bar"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), + _allow_exec_host_cmd: false + }); + expected_plugin_configuration.insert(PluginTag::new("strider"), PluginConfig { + path: PathBuf::from("strider"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("strider")), + _allow_exec_host_cmd: true + }); + expected_plugin_configuration.insert(PluginTag::new("compact-bar"), PluginConfig { + path: PathBuf::from("compact-bar"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("compact-bar")), + _allow_exec_host_cmd: false + }); + assert_eq!(config.plugins, PluginsConfig::from_data(expected_plugin_configuration), "Plugins defined in config"); + } + + #[test] + fn can_define_ui_configuration_in_configfile() { + let config_contents = r#" + ui { + pane_frames { + rounded_corners true + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let expected_ui_config = UiConfig { + pane_frames: FrameConfig { + rounded_corners: true + } + }; + assert_eq!(config.ui, Some(expected_ui_config), "Ui config defined in config"); + } + + #[test] + fn can_define_env_variables_in_config_file() { + let config_contents = r#" + env { + RUST_BACKTRACE 1 + SOME_OTHER_VAR "foo" + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let mut expected_env_config = HashMap::new(); + expected_env_config.insert("RUST_BACKTRACE".into(), "1".into()); + expected_env_config.insert("SOME_OTHER_VAR".into(), "foo".into()); + assert_eq!(config.env, EnvironmentVariables::from_data(expected_env_config), "Env variables defined in config"); + } + } diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index af56169690..4730d835b5 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -65,78 +65,78 @@ enum Unbind { All(bool), } -impl Default for Keybinds { - // Use once per codepath - // TODO investigate why - fn default() -> Keybinds { - Self::from_default_assets() - } -} +// impl Default for Keybinds { +// // Use once per codepath +// // TODO investigate why +// fn default() -> HashMap>> { +// Self::from_default_assets() +// } +// } impl Keybinds { pub fn new() -> Keybinds { Keybinds(HashMap::::new()) } - fn from_default_assets() -> Keybinds { + fn from_default_assets() -> HashMap>> { config::Config::from_default_assets() .expect("Keybinds from default assets Error!") .keybinds } - /// Entrypoint from the config module - pub fn get_default_keybinds_with_config(from_yaml: Option) -> Keybinds { - let default_keybinds = match from_yaml.clone() { - Some(keybinds) => match keybinds.unbind { - Unbind::All(true) => Keybinds::new(), - Unbind::All(false) | Unbind::Keys(_) => Keybinds::unbind(keybinds), - }, - None => Keybinds::default(), - }; - - if let Some(keybinds) = from_yaml { - default_keybinds.merge_keybinds(Keybinds::from(keybinds)) - } else { - default_keybinds - } - } +// /// Entrypoint from the config module +// pub fn get_default_keybinds_with_config(from_yaml: Option) -> Keybinds { +// let default_keybinds = match from_yaml.clone() { +// Some(keybinds) => match keybinds.unbind { +// Unbind::All(true) => Keybinds::new(), +// Unbind::All(false) | Unbind::Keys(_) => Keybinds::unbind(keybinds), +// }, +// None => Keybinds::default(), +// }; +// +// if let Some(keybinds) = from_yaml { +// default_keybinds.merge_keybinds(Keybinds::from(keybinds)) +// } else { +// default_keybinds +// } +// } /// Unbinds the default keybindings in relation to their mode - fn unbind(from_yaml: KeybindsFromYaml) -> Keybinds { - let mut keybind_config = Self::new(); - let mut unbind_config: HashMap = HashMap::new(); - let keybinds_from_yaml = from_yaml.keybinds; - - for mode in InputMode::iter() { - if let Some(keybinds) = keybinds_from_yaml.get(&mode) { - for keybind in keybinds { - match keybind { - KeyActionUnbind::Unbind(unbind) => { - unbind_config.insert(mode, unbind.unbind.clone()); - }, - KeyActionUnbind::KeyAction(key_action_from_yaml) => { - keybind_config - .0 - .insert(mode, ModeKeybinds::from(key_action_from_yaml.clone())); - }, - } - } - } - } - - let mut default = Self::default().unbind_mode(unbind_config); - - // Toplevel Unbinds - if let Unbind::Keys(_) = from_yaml.unbind { - let mut unbind_config: HashMap = HashMap::new(); - for mode in InputMode::iter() { - unbind_config.insert(mode, from_yaml.unbind.clone()); - } - default = default.unbind_mode(unbind_config); - }; - - default.merge_keybinds(keybind_config) - } +// fn unbind(from_yaml: KeybindsFromYaml) -> Keybinds { +// let mut keybind_config = Self::new(); +// let mut unbind_config: HashMap = HashMap::new(); +// let keybinds_from_yaml = from_yaml.keybinds; +// +// for mode in InputMode::iter() { +// if let Some(keybinds) = keybinds_from_yaml.get(&mode) { +// for keybind in keybinds { +// match keybind { +// KeyActionUnbind::Unbind(unbind) => { +// unbind_config.insert(mode, unbind.unbind.clone()); +// }, +// KeyActionUnbind::KeyAction(key_action_from_yaml) => { +// keybind_config +// .0 +// .insert(mode, ModeKeybinds::from(key_action_from_yaml.clone())); +// }, +// } +// } +// } +// } +// +// let mut default = Self::default().unbind_mode(unbind_config); +// +// // Toplevel Unbinds +// if let Unbind::Keys(_) = from_yaml.unbind { +// let mut unbind_config: HashMap = HashMap::new(); +// for mode in InputMode::iter() { +// unbind_config.insert(mode, from_yaml.unbind.clone()); +// } +// default = default.unbind_mode(unbind_config); +// }; +// +// default.merge_keybinds(keybind_config) +// } /// Unbind [`Key`] bindings respective to their mode fn unbind_mode(&self, unbind: HashMap) -> Keybinds { @@ -192,20 +192,25 @@ impl Keybinds { key: &Key, raw_bytes: Vec, mode: &InputMode, - keybinds: &Keybinds, + keybinds: &HashMap>>, // TODO: make this a type ) -> Vec { let mode_keybind_or_action = |action: Action| { - keybinds - .0 - .get(mode) - .unwrap_or({ - // create a dummy mode to recover from - &ModeKeybinds::new() - }) - .0 - .get(key) - .cloned() - .unwrap_or_else(|| vec![action]) + if let Some(mode) = keybinds.get(mode) { + if let Some(actions) = mode.get(key).cloned() { + return actions; + } + } + return vec![action]; +// keybinds +// .get(mode) +// .unwrap_or({ +// // create a dummy mode to recover from +// &ModeKeybinds::new() +// }) +// .0 +// .get(key) +// .cloned() +// .unwrap_or_else(|| vec![action]) }; match *mode { InputMode::Normal | InputMode::Locked => { diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 8af9c8a4d5..c5ff69592d 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -118,6 +118,17 @@ impl Default for Clipboard { } } +impl FromStr for Clipboard { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "System" | "system" => Ok(Self::System), + "Primary" | "primary" => Ok(Self::Primary), + _ => Err(format!("No such clipboard: {}", s)), + } + } +} + impl Options { pub fn from_yaml(from_yaml: Option) -> Options { if let Some(opts) = from_yaml { diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index 8dec828d56..bb732c618d 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -15,13 +15,13 @@ use super::layout::{RunPlugin, RunPluginLocation}; pub use crate::data::PluginTag; use crate::setup; -lazy_static! { - static ref DEFAULT_CONFIG_PLUGINS: PluginsConfig = { - let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec()).unwrap(); - let cfg_yaml: ConfigFromYaml = serde_yaml::from_str(&cfg).unwrap(); - PluginsConfig::try_from(cfg_yaml.plugins).unwrap() - }; -} +// lazy_static! { +// static ref DEFAULT_CONFIG_PLUGINS: PluginsConfig = { +// let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec()).unwrap(); +// let cfg_yaml: ConfigFromYaml = serde_yaml::from_str(&cfg).unwrap(); +// PluginsConfig::try_from(cfg_yaml.plugins).unwrap() +// }; +// } #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] pub struct PluginsConfigFromYaml(Vec); @@ -34,13 +34,16 @@ impl PluginsConfig { pub fn new() -> Self { Self(HashMap::new()) } + pub fn from_data(data: HashMap) -> Self { + PluginsConfig(data) + } /// Entrypoint from the config module - pub fn get_plugins_with_default(user_plugins: Self) -> Self { - let mut base_plugins = DEFAULT_CONFIG_PLUGINS.clone(); - base_plugins.0.extend(user_plugins.0); - base_plugins - } +// pub fn get_plugins_with_default(user_plugins: Self) -> Self { +// let mut base_plugins = DEFAULT_CONFIG_PLUGINS.clone(); +// base_plugins.0.extend(user_plugins.0); +// base_plugins +// } /// Get plugin config from run configuration specified in layout files. pub fn get(&self, run: impl Borrow) -> Option { @@ -74,7 +77,7 @@ impl PluginsConfig { impl Default for PluginsConfig { fn default() -> Self { - Self::get_plugins_with_default(PluginsConfig::new()) + PluginsConfig(HashMap::new()) } } @@ -207,98 +210,100 @@ pub enum PluginsConfigError { InvalidPluginLocation(PathBuf), } -#[cfg(test)] -mod tests { - use super::*; - use crate::input::config::ConfigError; - use std::convert::TryInto; - - #[test] - fn run_plugin_permissions_are_inherited() -> Result<(), ConfigError> { - let yaml_plugins: PluginsConfigFromYaml = serde_yaml::from_str( - " - - path: boo.wasm - tag: boo - _allow_exec_host_cmd: false - ", - )?; - let plugins = PluginsConfig::try_from(yaml_plugins)?; - - assert_eq!( - plugins.get(RunPlugin { - _allow_exec_host_cmd: true, - location: RunPluginLocation::Zellij(PluginTag::new("boo")) - }), - Some(PluginConfig { - _allow_exec_host_cmd: true, - path: PathBuf::from("boo.wasm"), - location: RunPluginLocation::Zellij(PluginTag::new("boo")), - run: PluginType::Pane(None), - }) - ); - - Ok(()) - } - - #[test] - fn try_from_yaml_fails_when_duplicate_tag_names_are_present() -> Result<(), ConfigError> { - let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( - " - plugins: - - path: /foo/bar/baz.wasm - tag: boo - - path: /foo/bar/boo.wasm - tag: boo - ", - )?; - - assert_eq!( - PluginsConfig::try_from(plugins), - Err(PluginsConfigError::DuplicatePlugins(PluginTag::new("boo"))) - ); - - Ok(()) - } - - #[test] - fn default_plugins() -> Result<(), ConfigError> { - let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( - " - plugins: - - path: boo.wasm - tag: boo - ", - )?; - let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?); - - assert_eq!(plugins.iter().count(), 5); - Ok(()) - } - - #[test] - fn default_plugins_allow_overriding() -> Result<(), ConfigError> { - let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( - " - plugins: - - path: boo.wasm - tag: tab-bar - ", - )?; - let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?); - - assert_eq!( - plugins.get(RunPlugin { - _allow_exec_host_cmd: false, - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")) - }), - Some(PluginConfig { - _allow_exec_host_cmd: false, - path: PathBuf::from("boo.wasm"), - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - run: PluginType::Pane(None), - }) - ); - - Ok(()) - } -} +// TODO: make sure all these cases are tested +// +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::input::config::ConfigError; +// use std::convert::TryInto; +// +// #[test] +// fn run_plugin_permissions_are_inherited() -> Result<(), ConfigError> { +// let yaml_plugins: PluginsConfigFromYaml = serde_yaml::from_str( +// " +// - path: boo.wasm +// tag: boo +// _allow_exec_host_cmd: false +// ", +// )?; +// let plugins = PluginsConfig::try_from(yaml_plugins)?; +// +// assert_eq!( +// plugins.get(RunPlugin { +// _allow_exec_host_cmd: true, +// location: RunPluginLocation::Zellij(PluginTag::new("boo")) +// }), +// Some(PluginConfig { +// _allow_exec_host_cmd: true, +// path: PathBuf::from("boo.wasm"), +// location: RunPluginLocation::Zellij(PluginTag::new("boo")), +// run: PluginType::Pane(None), +// }) +// ); +// +// Ok(()) +// } +// +// #[test] +// fn try_from_yaml_fails_when_duplicate_tag_names_are_present() -> Result<(), ConfigError> { +// let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( +// " +// plugins: +// - path: /foo/bar/baz.wasm +// tag: boo +// - path: /foo/bar/boo.wasm +// tag: boo +// ", +// )?; +// +// assert_eq!( +// PluginsConfig::try_from(plugins), +// Err(PluginsConfigError::DuplicatePlugins(PluginTag::new("boo"))) +// ); +// +// Ok(()) +// } +// +// #[test] +// fn default_plugins() -> Result<(), ConfigError> { +// let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( +// " +// plugins: +// - path: boo.wasm +// tag: boo +// ", +// )?; +// let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?); +// +// assert_eq!(plugins.iter().count(), 5); +// Ok(()) +// } +// +// #[test] +// fn default_plugins_allow_overriding() -> Result<(), ConfigError> { +// let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( +// " +// plugins: +// - path: boo.wasm +// tag: tab-bar +// ", +// )?; +// let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?); +// +// assert_eq!( +// plugins.get(RunPlugin { +// _allow_exec_host_cmd: false, +// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")) +// }), +// Some(PluginConfig { +// _allow_exec_host_cmd: false, +// path: PathBuf::from("boo.wasm"), +// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// run: PluginType::Pane(None), +// }) +// ); +// +// Ok(()) +// } +// } diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index fc8329a7e9..218977344f 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -13,19 +13,20 @@ use crate::shared::detect_theme_hue; pub struct ThemesFromYaml(HashMap); #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] -pub struct UiConfigFromYaml { - pub pane_frames: FrameConfigFromYaml, +pub struct UiConfig { + pub pane_frames: FrameConfig, } #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] -pub struct FrameConfigFromYaml { +pub struct FrameConfig { pub rounded_corners: bool, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -struct Theme { +pub struct Theme { #[serde(flatten)] - palette: PaletteFromYaml, + // palette: PaletteFromYaml, + pub palette: Palette, } /// Intermediate deserialization struct @@ -141,7 +142,7 @@ impl ThemesFromYaml { fn from_default_theme(&mut self, theme: String) -> Option { self.clone() .get_theme(theme) - .map(|t| Palette::from(t.palette)) + .map(|t| t.palette) } /// Merges two Theme structs into one Theme struct diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index ed13289819..d844a75bf1 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -3,809 +3,811 @@ use super::super::keybinds::*; use crate::data::Key; use crate::input::CharOrArrow; -#[test] -fn merge_keybinds_merges_different_keys() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::Backspace, vec![Action::NoOp]); - - let mut mode_keybinds_expected = ModeKeybinds::new(); - mode_keybinds_expected - .0 - .insert(Key::F(1), vec![Action::NoOp]); - mode_keybinds_expected - .0 - .insert(Key::Backspace, vec![Action::NoOp]); - - let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); - - assert_eq!(mode_keybinds_expected, mode_keybinds_merged); -} - -#[test] -fn merge_mode_keybinds_overwrites_same_keys() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::F(1), vec![Action::GoToTab(1)]); - - let mut mode_keybinds_expected = ModeKeybinds::new(); - mode_keybinds_expected - .0 - .insert(Key::F(1), vec![Action::GoToTab(1)]); - - let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); - - assert_eq!(mode_keybinds_expected, mode_keybinds_merged); -} - -#[test] -fn merge_keybinds_merges() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::Backspace, vec![Action::NoOp]); - let mut keybinds_self = Keybinds::new(); - keybinds_self - .0 - .insert(InputMode::Normal, mode_keybinds_self.clone()); - let mut keybinds_other = Keybinds::new(); - keybinds_other - .0 - .insert(InputMode::Resize, mode_keybinds_other.clone()); - let mut keybinds_expected = Keybinds::new(); - keybinds_expected - .0 - .insert(InputMode::Normal, mode_keybinds_self); - keybinds_expected - .0 - .insert(InputMode::Resize, mode_keybinds_other); - - assert_eq!( - keybinds_expected, - keybinds_self.merge_keybinds(keybinds_other) - ) -} - -#[test] -fn merge_keybinds_overwrites_same_keys() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - mode_keybinds_self.0.insert(Key::F(2), vec![Action::NoOp]); - mode_keybinds_self.0.insert(Key::F(3), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::F(1), vec![Action::GoToTab(1)]); - mode_keybinds_other - .0 - .insert(Key::F(2), vec![Action::GoToTab(2)]); - mode_keybinds_other - .0 - .insert(Key::F(3), vec![Action::GoToTab(3)]); - let mut keybinds_self = Keybinds::new(); - keybinds_self - .0 - .insert(InputMode::Normal, mode_keybinds_self); - let mut keybinds_other = Keybinds::new(); - keybinds_other - .0 - .insert(InputMode::Normal, mode_keybinds_other.clone()); - let mut keybinds_expected = Keybinds::new(); - keybinds_expected - .0 - .insert(InputMode::Normal, mode_keybinds_other); - - assert_eq!( - keybinds_expected, - keybinds_self.merge_keybinds(keybinds_other) - ) -} - -#[test] -fn from_keyaction_from_yaml_to_mode_keybindings() { - let actions = vec![Action::NoOp, Action::GoToTab(1)]; - let keyaction = KeyActionFromYaml { - action: actions.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - - let mut expected = ModeKeybinds::new(); - expected.0.insert(Key::F(1), actions.clone()); - expected.0.insert(Key::Backspace, actions.clone()); - expected.0.insert(Key::Char('t'), actions); - - assert_eq!(expected, ModeKeybinds::from(keyaction)); -} - -#[test] -fn toplevel_unbind_unbinds_all() { - let from_yaml = KeybindsFromYaml { - unbind: Unbind::All(true), - keybinds: HashMap::new(), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn no_unbind_unbinds_none() { - let from_yaml = KeybindsFromYaml { - unbind: Unbind::All(false), - keybinds: HashMap::new(), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - assert_eq!(keybinds_from_yaml, Keybinds::default()); -} - -#[test] -fn last_keybind_is_taken() { - let actions_1 = vec![Action::NoOp, Action::NewTab(None)]; - let keyaction_1 = KeyActionFromYaml { - action: actions_1, - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - let actions_2 = vec![Action::GoToTab(1)]; - let keyaction_2 = KeyActionFromYaml { - action: actions_2.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - - let mut expected = ModeKeybinds::new(); - expected.0.insert(Key::F(1), actions_2.clone()); - expected.0.insert(Key::Backspace, actions_2.clone()); - expected.0.insert(Key::Char('t'), actions_2); - - assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); -} - -#[test] -fn last_keybind_overwrites() { - let actions_1 = vec![Action::NoOp, Action::NewTab(None)]; - let keyaction_1 = KeyActionFromYaml { - action: actions_1.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - let actions_2 = vec![Action::GoToTab(1)]; - let keyaction_2 = KeyActionFromYaml { - action: actions_2.clone(), - key: vec![Key::F(1), Key::Char('t')], - }; - - let mut expected = ModeKeybinds::new(); - expected.0.insert(Key::F(1), actions_2.clone()); - expected.0.insert(Key::Backspace, actions_1); - expected.0.insert(Key::Char('t'), actions_2); - - assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); -} - -#[test] -fn unbind_single_mode() { - let unbind = Unbind::All(true); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let result = keybinds.0.get(&InputMode::Normal); - assert!(result.is_none()); -} - -#[test] -fn unbind_multiple_modes() { - let unbind = Unbind::All(true); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds.clone()); - keys.insert(InputMode::Pane, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let normal = keybinds.0.get(&InputMode::Normal); - let pane = keybinds.0.get(&InputMode::Pane); - assert!(normal.is_none()); - assert!(pane.is_none()); -} - -#[test] -fn unbind_single_keybind_single_mode() { - let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds = keybinds.0.get(&InputMode::Normal); - let result = mode_keybinds - .expect("Mode shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - assert!(result.is_none()); -} - -#[test] -fn unbind_single_keybind_multiple_modes() { - let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); - let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); - let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; - let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; - let key_action_unbinds_n = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; - let key_action_unbinds_h = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; - - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_n); - keys.insert(InputMode::Pane, key_action_unbinds_h); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let normal = keybinds.0.get(&InputMode::Normal); - let pane = keybinds.0.get(&InputMode::Pane); - let result_normal = normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_pane = pane - .expect("Mode shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('h'))); - assert!(result_normal.is_none()); - assert!(result_pane.is_none()); -} - -#[test] -fn unbind_multiple_keybinds_single_mode() { - let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds = keybinds.0.get(&InputMode::Normal); - let result_n = mode_keybinds - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_p = mode_keybinds - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - assert!(result_n.is_none()); - assert!(result_p.is_none()); -} - -#[test] -fn unbind_multiple_keybinds_multiple_modes() { - let unbind_normal = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); - let unbind_resize = Unbind::Keys(vec![Key::Char('h'), Key::Ctrl('r')]); - let unbind_from_yaml_normal = UnbindFromYaml { - unbind: unbind_normal, - }; - let unbind_from_yaml_resize = UnbindFromYaml { - unbind: unbind_resize, - }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; - let key_action_unbinds_resize = vec![KeyActionUnbind::Unbind(unbind_from_yaml_resize)]; - - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Resize, key_action_unbinds_resize); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); - let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); - let result_normal_1 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_normal_2 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - let result_resize_1 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('h')); - let result_resize_2 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('r')); - assert!(result_normal_1.is_none()); - assert!(result_resize_1.is_none()); - assert!(result_normal_2.is_none()); - assert!(result_resize_2.is_none()); -} - -#[test] -fn unbind_multiple_keybinds_all_modes() { - let unbind = Unbind::Keys(vec![ - Key::Alt(CharOrArrow::Char('n')), - Key::Alt(CharOrArrow::Char('h')), - ]); - let keys = HashMap::>::new(); - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind, - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); - let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); - let result_normal_1 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_normal_2 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('f')); - let result_resize_1 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('n')); - let result_resize_2 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('f')); - assert!(result_normal_1.is_none()); - assert!(result_resize_1.is_none()); - assert!(result_normal_2.is_none()); - assert!(result_resize_2.is_none()); -} - -#[test] -fn unbind_all_toplevel_single_key_single_mode() { - let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_all_toplevel_single_key_multiple_modes() { - let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); - let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); - let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; - let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; - let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Pane, key_action_unbinds_pane); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_all_toplevel_multiple_key_multiple_modes() { - let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); - let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h')), Key::Ctrl('t')]); - let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; - let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; - let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Pane, key_action_unbinds_pane); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_all_toplevel_all_key_multiple_modes() { - let unbind = Unbind::All(true); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml.clone())]; - let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Pane, key_action_unbinds_pane); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_single_keybind_all_modes() { - let keys = HashMap::>::new(); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_pane = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_resize = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_tab = keybinds_from_yaml - .0 - .get(&InputMode::Tab) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - - assert!(result_normal.is_none()); - assert!(result_pane.is_none()); - assert!(result_resize.is_none()); - assert!(result_tab.is_none()); -} - -#[test] -fn unbind_single_toplevel_single_key_single_mode_identical() { - let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_pane = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_resize = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_tab = keybinds_from_yaml - .0 - .get(&InputMode::Tab) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - - assert!(result_normal.is_none()); - assert!(result_pane.is_none()); - assert!(result_resize.is_none()); - assert!(result_tab.is_none()); -} - -#[test] -fn unbind_single_toplevel_single_key_single_mode_differing() { - let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l'))]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - let result_resize_n = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_resize_l = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_resize_n.is_none()); - assert!(result_resize_l.is_some()); -} - -#[test] -fn unbind_single_toplevel_single_key_multiple_modes() { - let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l'))]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbind.clone()); - keys.insert(InputMode::Pane, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - let result_pane_n = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_pane_l = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_pane_n.is_none()); - assert!(result_pane_l.is_none()); -} - -#[test] -fn unbind_single_toplevel_multiple_keys_single_mode() { - let unbind = Unbind::Keys(vec![ - Key::Alt(CharOrArrow::Char('l')), - Key::Alt(CharOrArrow::Char('h')), - Key::Alt(CharOrArrow::Char('j')), - Key::Alt(CharOrArrow::Char('k')), - ]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbind.clone()); - keys.insert(InputMode::Pane, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - let result_normal_k = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('k'))); - let result_normal_h = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('h'))); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_normal_h.is_none()); - assert!(result_normal_k.is_none()); -} - -#[test] -fn unbind_single_toplevel_multiple_keys_multiple_modes() { - let unbind_normal = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l')), Key::Ctrl('p')]); - let unbind_from_yaml_normal = UnbindFromYaml { - unbind: unbind_normal, - }; - let key_action_unbind_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; - let unbind = Unbind::Keys(vec![ - Key::Alt(CharOrArrow::Char('l')), - Key::Alt(CharOrArrow::Char('k')), - ]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbind_normal); - keys.insert(InputMode::Pane, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_normal_p = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - let result_pane_p = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - let result_pane_n = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('n'))); - let result_pane_l = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt(CharOrArrow::Char('l'))); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_normal_p.is_none()); - assert!(result_pane_n.is_none()); - assert!(result_pane_p.is_some()); - assert!(result_pane_l.is_none()); -} - -#[test] -fn uppercase_and_lowercase_are_distinct() { - let key_action_n = KeyActionFromYaml { - key: vec![Key::Char('n')], - action: vec![Action::NewTab(None)], - }; - let key_action_large_n = KeyActionFromYaml { - key: vec![Key::Char('N')], - action: vec![Action::NewPane(None)], - }; - - let key_action_unbind = vec![ - KeyActionUnbind::KeyAction(key_action_n), - KeyActionUnbind::KeyAction(key_action_large_n), - ]; - let mut keys = HashMap::>::new(); - keys.insert(InputMode::Normal, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - let result_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('n')); - let result_large_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('N')); - - assert!(result_n.is_some()); - assert!(result_large_n.is_some()); -} +// TODO: make sure these are all covered + +// #[test] +// fn merge_keybinds_merges_different_keys() { +// let mut mode_keybinds_self = ModeKeybinds::new(); +// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); +// let mut mode_keybinds_other = ModeKeybinds::new(); +// mode_keybinds_other +// .0 +// .insert(Key::Backspace, vec![Action::NoOp]); +// +// let mut mode_keybinds_expected = ModeKeybinds::new(); +// mode_keybinds_expected +// .0 +// .insert(Key::F(1), vec![Action::NoOp]); +// mode_keybinds_expected +// .0 +// .insert(Key::Backspace, vec![Action::NoOp]); +// +// let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); +// +// assert_eq!(mode_keybinds_expected, mode_keybinds_merged); +// } +// +// #[test] +// fn merge_mode_keybinds_overwrites_same_keys() { +// let mut mode_keybinds_self = ModeKeybinds::new(); +// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); +// let mut mode_keybinds_other = ModeKeybinds::new(); +// mode_keybinds_other +// .0 +// .insert(Key::F(1), vec![Action::GoToTab(1)]); +// +// let mut mode_keybinds_expected = ModeKeybinds::new(); +// mode_keybinds_expected +// .0 +// .insert(Key::F(1), vec![Action::GoToTab(1)]); +// +// let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); +// +// assert_eq!(mode_keybinds_expected, mode_keybinds_merged); +// } +// +// #[test] +// fn merge_keybinds_merges() { +// let mut mode_keybinds_self = ModeKeybinds::new(); +// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); +// let mut mode_keybinds_other = ModeKeybinds::new(); +// mode_keybinds_other +// .0 +// .insert(Key::Backspace, vec![Action::NoOp]); +// let mut keybinds_self = Keybinds::new(); +// keybinds_self +// .0 +// .insert(InputMode::Normal, mode_keybinds_self.clone()); +// let mut keybinds_other = Keybinds::new(); +// keybinds_other +// .0 +// .insert(InputMode::Resize, mode_keybinds_other.clone()); +// let mut keybinds_expected = Keybinds::new(); +// keybinds_expected +// .0 +// .insert(InputMode::Normal, mode_keybinds_self); +// keybinds_expected +// .0 +// .insert(InputMode::Resize, mode_keybinds_other); +// +// assert_eq!( +// keybinds_expected, +// keybinds_self.merge_keybinds(keybinds_other) +// ) +// } +// +// #[test] +// fn merge_keybinds_overwrites_same_keys() { +// let mut mode_keybinds_self = ModeKeybinds::new(); +// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); +// mode_keybinds_self.0.insert(Key::F(2), vec![Action::NoOp]); +// mode_keybinds_self.0.insert(Key::F(3), vec![Action::NoOp]); +// let mut mode_keybinds_other = ModeKeybinds::new(); +// mode_keybinds_other +// .0 +// .insert(Key::F(1), vec![Action::GoToTab(1)]); +// mode_keybinds_other +// .0 +// .insert(Key::F(2), vec![Action::GoToTab(2)]); +// mode_keybinds_other +// .0 +// .insert(Key::F(3), vec![Action::GoToTab(3)]); +// let mut keybinds_self = Keybinds::new(); +// keybinds_self +// .0 +// .insert(InputMode::Normal, mode_keybinds_self); +// let mut keybinds_other = Keybinds::new(); +// keybinds_other +// .0 +// .insert(InputMode::Normal, mode_keybinds_other.clone()); +// let mut keybinds_expected = Keybinds::new(); +// keybinds_expected +// .0 +// .insert(InputMode::Normal, mode_keybinds_other); +// +// assert_eq!( +// keybinds_expected, +// keybinds_self.merge_keybinds(keybinds_other) +// ) +// } +// +// #[test] +// fn from_keyaction_from_yaml_to_mode_keybindings() { +// let actions = vec![Action::NoOp, Action::GoToTab(1)]; +// let keyaction = KeyActionFromYaml { +// action: actions.clone(), +// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], +// }; +// +// let mut expected = ModeKeybinds::new(); +// expected.0.insert(Key::F(1), actions.clone()); +// expected.0.insert(Key::Backspace, actions.clone()); +// expected.0.insert(Key::Char('t'), actions); +// +// assert_eq!(expected, ModeKeybinds::from(keyaction)); +// } +// +// #[test] +// fn toplevel_unbind_unbinds_all() { +// let from_yaml = KeybindsFromYaml { +// unbind: Unbind::All(true), +// keybinds: HashMap::new(), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// assert_eq!(keybinds_from_yaml, Keybinds::new()); +// } +// +// #[test] +// fn no_unbind_unbinds_none() { +// let from_yaml = KeybindsFromYaml { +// unbind: Unbind::All(false), +// keybinds: HashMap::new(), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// assert_eq!(keybinds_from_yaml, Keybinds::default()); +// } +// +// #[test] +// fn last_keybind_is_taken() { +// let actions_1 = vec![Action::NoOp, Action::NewTab(None)]; +// let keyaction_1 = KeyActionFromYaml { +// action: actions_1, +// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], +// }; +// let actions_2 = vec![Action::GoToTab(1)]; +// let keyaction_2 = KeyActionFromYaml { +// action: actions_2.clone(), +// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], +// }; +// +// let mut expected = ModeKeybinds::new(); +// expected.0.insert(Key::F(1), actions_2.clone()); +// expected.0.insert(Key::Backspace, actions_2.clone()); +// expected.0.insert(Key::Char('t'), actions_2); +// +// assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); +// } +// +// #[test] +// fn last_keybind_overwrites() { +// let actions_1 = vec![Action::NoOp, Action::NewTab(None)]; +// let keyaction_1 = KeyActionFromYaml { +// action: actions_1.clone(), +// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], +// }; +// let actions_2 = vec![Action::GoToTab(1)]; +// let keyaction_2 = KeyActionFromYaml { +// action: actions_2.clone(), +// key: vec![Key::F(1), Key::Char('t')], +// }; +// +// let mut expected = ModeKeybinds::new(); +// expected.0.insert(Key::F(1), actions_2.clone()); +// expected.0.insert(Key::Backspace, actions_1); +// expected.0.insert(Key::Char('t'), actions_2); +// +// assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); +// } +// +// #[test] +// fn unbind_single_mode() { +// let unbind = Unbind::All(true); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds); +// +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let result = keybinds.0.get(&InputMode::Normal); +// assert!(result.is_none()); +// } +// +// #[test] +// fn unbind_multiple_modes() { +// let unbind = Unbind::All(true); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds.clone()); +// keys.insert(InputMode::Pane, key_action_unbinds); +// +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let normal = keybinds.0.get(&InputMode::Normal); +// let pane = keybinds.0.get(&InputMode::Pane); +// assert!(normal.is_none()); +// assert!(pane.is_none()); +// } +// +// #[test] +// fn unbind_single_keybind_single_mode() { +// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds); +// +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let mode_keybinds = keybinds.0.get(&InputMode::Normal); +// let result = mode_keybinds +// .expect("Mode shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// assert!(result.is_none()); +// } +// +// #[test] +// fn unbind_single_keybind_multiple_modes() { +// let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); +// let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); +// let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; +// let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; +// let key_action_unbinds_n = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; +// let key_action_unbinds_h = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; +// +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds_n); +// keys.insert(InputMode::Pane, key_action_unbinds_h); +// +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let normal = keybinds.0.get(&InputMode::Normal); +// let pane = keybinds.0.get(&InputMode::Pane); +// let result_normal = normal +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_pane = pane +// .expect("Mode shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('h'))); +// assert!(result_normal.is_none()); +// assert!(result_pane.is_none()); +// } +// +// #[test] +// fn unbind_multiple_keybinds_single_mode() { +// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds); +// +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let mode_keybinds = keybinds.0.get(&InputMode::Normal); +// let result_n = mode_keybinds +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_p = mode_keybinds +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('p')); +// assert!(result_n.is_none()); +// assert!(result_p.is_none()); +// } +// +// #[test] +// fn unbind_multiple_keybinds_multiple_modes() { +// let unbind_normal = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); +// let unbind_resize = Unbind::Keys(vec![Key::Char('h'), Key::Ctrl('r')]); +// let unbind_from_yaml_normal = UnbindFromYaml { +// unbind: unbind_normal, +// }; +// let unbind_from_yaml_resize = UnbindFromYaml { +// unbind: unbind_resize, +// }; +// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; +// let key_action_unbinds_resize = vec![KeyActionUnbind::Unbind(unbind_from_yaml_resize)]; +// +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds_normal); +// keys.insert(InputMode::Resize, key_action_unbinds_resize); +// +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); +// let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); +// let result_normal_1 = mode_keybinds_normal +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_normal_2 = mode_keybinds_normal +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('p')); +// let result_resize_1 = mode_keybinds_resize +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Char('h')); +// let result_resize_2 = mode_keybinds_resize +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('r')); +// assert!(result_normal_1.is_none()); +// assert!(result_resize_1.is_none()); +// assert!(result_normal_2.is_none()); +// assert!(result_resize_2.is_none()); +// } +// +// #[test] +// fn unbind_multiple_keybinds_all_modes() { +// let unbind = Unbind::Keys(vec![ +// Key::Alt(CharOrArrow::Char('n')), +// Key::Alt(CharOrArrow::Char('h')), +// ]); +// let keys = HashMap::>::new(); +// let keybinds_from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind, +// }; +// +// let keybinds = Keybinds::unbind(keybinds_from_yaml); +// let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); +// let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); +// let result_normal_1 = mode_keybinds_normal +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_normal_2 = mode_keybinds_normal +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('f')); +// let result_resize_1 = mode_keybinds_resize +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Char('n')); +// let result_resize_2 = mode_keybinds_resize +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('f')); +// assert!(result_normal_1.is_none()); +// assert!(result_resize_1.is_none()); +// assert!(result_normal_2.is_none()); +// assert!(result_resize_2.is_none()); +// } +// +// #[test] +// fn unbind_all_toplevel_single_key_single_mode() { +// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds_normal); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(true), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// assert_eq!(keybinds_from_yaml, Keybinds::new()); +// } +// +// #[test] +// fn unbind_all_toplevel_single_key_multiple_modes() { +// let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); +// let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); +// let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; +// let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; +// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; +// let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds_normal); +// keys.insert(InputMode::Pane, key_action_unbinds_pane); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(true), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// assert_eq!(keybinds_from_yaml, Keybinds::new()); +// } +// +// #[test] +// fn unbind_all_toplevel_multiple_key_multiple_modes() { +// let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); +// let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h')), Key::Ctrl('t')]); +// let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; +// let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; +// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; +// let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds_normal); +// keys.insert(InputMode::Pane, key_action_unbinds_pane); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(true), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// assert_eq!(keybinds_from_yaml, Keybinds::new()); +// } +// +// #[test] +// fn unbind_all_toplevel_all_key_multiple_modes() { +// let unbind = Unbind::All(true); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml.clone())]; +// let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbinds_normal); +// keys.insert(InputMode::Pane, key_action_unbinds_pane); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(true), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// assert_eq!(keybinds_from_yaml, Keybinds::new()); +// } +// +// #[test] +// fn unbind_single_keybind_all_modes() { +// let keys = HashMap::>::new(); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// let result_normal = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_pane = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_resize = keybinds_from_yaml +// .0 +// .get(&InputMode::Resize) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_tab = keybinds_from_yaml +// .0 +// .get(&InputMode::Tab) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// +// assert!(result_normal.is_none()); +// assert!(result_pane.is_none()); +// assert!(result_resize.is_none()); +// assert!(result_tab.is_none()); +// } +// +// #[test] +// fn unbind_single_toplevel_single_key_single_mode_identical() { +// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbind); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// let result_normal = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_pane = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_resize = keybinds_from_yaml +// .0 +// .get(&InputMode::Resize) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_tab = keybinds_from_yaml +// .0 +// .get(&InputMode::Tab) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// +// assert!(result_normal.is_none()); +// assert!(result_pane.is_none()); +// assert!(result_resize.is_none()); +// assert!(result_tab.is_none()); +// } +// +// #[test] +// fn unbind_single_toplevel_single_key_single_mode_differing() { +// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l'))]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbind); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// let result_normal_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_normal_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// let result_resize_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Resize) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_resize_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Resize) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// +// assert!(result_normal_n.is_none()); +// assert!(result_normal_l.is_none()); +// assert!(result_resize_n.is_none()); +// assert!(result_resize_l.is_some()); +// } +// +// #[test] +// fn unbind_single_toplevel_single_key_multiple_modes() { +// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l'))]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbind.clone()); +// keys.insert(InputMode::Pane, key_action_unbind); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// let result_normal_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_normal_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// let result_pane_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_pane_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// +// assert!(result_normal_n.is_none()); +// assert!(result_normal_l.is_none()); +// assert!(result_pane_n.is_none()); +// assert!(result_pane_l.is_none()); +// } +// +// #[test] +// fn unbind_single_toplevel_multiple_keys_single_mode() { +// let unbind = Unbind::Keys(vec![ +// Key::Alt(CharOrArrow::Char('l')), +// Key::Alt(CharOrArrow::Char('h')), +// Key::Alt(CharOrArrow::Char('j')), +// Key::Alt(CharOrArrow::Char('k')), +// ]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbind.clone()); +// keys.insert(InputMode::Pane, key_action_unbind); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// let result_normal_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_normal_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// let result_normal_k = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('k'))); +// let result_normal_h = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('h'))); +// +// assert!(result_normal_n.is_none()); +// assert!(result_normal_l.is_none()); +// assert!(result_normal_h.is_none()); +// assert!(result_normal_k.is_none()); +// } +// +// #[test] +// fn unbind_single_toplevel_multiple_keys_multiple_modes() { +// let unbind_normal = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l')), Key::Ctrl('p')]); +// let unbind_from_yaml_normal = UnbindFromYaml { +// unbind: unbind_normal, +// }; +// let key_action_unbind_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; +// let unbind = Unbind::Keys(vec![ +// Key::Alt(CharOrArrow::Char('l')), +// Key::Alt(CharOrArrow::Char('k')), +// ]); +// let unbind_from_yaml = UnbindFromYaml { unbind }; +// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbind_normal); +// keys.insert(InputMode::Pane, key_action_unbind); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// +// let result_normal_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_normal_p = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('p')); +// let result_normal_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// let result_pane_p = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Ctrl('p')); +// let result_pane_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('n'))); +// let result_pane_l = keybinds_from_yaml +// .0 +// .get(&InputMode::Pane) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Alt(CharOrArrow::Char('l'))); +// +// assert!(result_normal_n.is_none()); +// assert!(result_normal_l.is_none()); +// assert!(result_normal_p.is_none()); +// assert!(result_pane_n.is_none()); +// assert!(result_pane_p.is_some()); +// assert!(result_pane_l.is_none()); +// } +// +// #[test] +// fn uppercase_and_lowercase_are_distinct() { +// let key_action_n = KeyActionFromYaml { +// key: vec![Key::Char('n')], +// action: vec![Action::NewTab(None)], +// }; +// let key_action_large_n = KeyActionFromYaml { +// key: vec![Key::Char('N')], +// action: vec![Action::NewPane(None)], +// }; +// +// let key_action_unbind = vec![ +// KeyActionUnbind::KeyAction(key_action_n), +// KeyActionUnbind::KeyAction(key_action_large_n), +// ]; +// let mut keys = HashMap::>::new(); +// keys.insert(InputMode::Normal, key_action_unbind); +// let from_yaml = KeybindsFromYaml { +// keybinds: keys, +// unbind: Unbind::All(false), +// }; +// +// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); +// let result_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Char('n')); +// let result_large_n = keybinds_from_yaml +// .0 +// .get(&InputMode::Normal) +// .expect("ModeKeybinds shouldn't be empty") +// .0 +// .get(&Key::Char('N')); +// +// assert!(result_n.is_some()); +// assert!(result_large_n.is_some()); +// } diff --git a/zellij-utils/src/kdl.rs b/zellij-utils/src/kdl.rs new file mode 100644 index 0000000000..c56d56df03 --- /dev/null +++ b/zellij-utils/src/kdl.rs @@ -0,0 +1,431 @@ +//! Definition of the actions that can be bound to keys. + +// use super::layout::TabLayout; +use crate::data::{InputMode, Key, CharOrArrow, PaletteColor}; +use crate::input::options::OnForceClose; +use serde::{Deserialize, Serialize}; + +use kdl::{KdlDocument, KdlValue}; + +use std::str::FromStr; +use std::path::PathBuf; + +use crate::position::Position; +use crate::input::actions::{Action, ResizeDirection, Direction}; +use crate::input::command::RunCommandAction; + +#[macro_export] +macro_rules! parse_kdl_action_arguments { + ( $action_name:expr, $action_arguments:expr ) => { + { + if !$action_arguments.is_empty() { + Err(format!("Failed to parse action: {}", $action_name)) + } else { + match $action_name { + "Quit" => Ok(Action::Quit), + "FocusNextPane" => Ok(Action::FocusNextPane), + "FocusPreviousPane" => Ok(Action::FocusPreviousPane), + "SwitchFocus" => Ok(Action::SwitchFocus), + "EditScrollback" => Ok(Action::EditScrollback), + "ScrollUp" => Ok(Action::ScrollUp), + "ScrollDown" => Ok(Action::ScrollDown), + "ScrollToBottom" => Ok(Action::ScrollToBottom), + "PageScrollUp" => Ok(Action::PageScrollUp), + "PageScrollDown" => Ok(Action::PageScrollDown), + "HalfPageScrollUp" => Ok(Action::HalfPageScrollUp), + "HalfPageScrollDown" => Ok(Action::HalfPageScrollDown), + "ToggleFocusFullscreen" => Ok(Action::ToggleFocusFullscreen), + "TogglePaneFrames" => Ok(Action::TogglePaneFrames), + "ToggleActiveSyncTab" => Ok(Action::ToggleActiveSyncTab), + "TogglePaneEmbedOrFloating" => Ok(Action::TogglePaneEmbedOrFloating), + "ToggleFloatingPanes" => Ok(Action::ToggleFloatingPanes), + "CloseFocus" => Ok(Action::CloseFocus), + "UndoRenamePane" => Ok(Action::UndoRenamePane), + "NoOp" => Ok(Action::NoOp), + "GoToNextTab" => Ok(Action::GoToNextTab), + "GoToPreviousTab" => Ok(Action::GoToPreviousTab), + "CloseTab" => Ok(Action::CloseTab), + "ToggleTab" => Ok(Action::ToggleTab), + "UndoRenameTab" => Ok(Action::UndoRenameTab), + "Detach" => Ok(Action::Detach), + "Copy" => Ok(Action::Copy), + "Confirm" => Ok(Action::Confirm), + "Deny" => Ok(Action::Deny), + _ => Err(format!("Error parsing enum variant: {:?}", $action_name)) + } + } + } + }; +} + +#[macro_export] +macro_rules! parse_kdl_action_u8_arguments { + ( $action_name:expr, $action_arguments:expr ) => {{ + let mut bytes = vec![]; + for kdl_value in $action_arguments.iter() { + match kdl_value.as_i64() { + Some(int_value) => bytes.push(int_value as u8), + None => { + return Err(format!("Failed to parse action: {}", $action_name)); + } + } + }; + Action::new_from_bytes($action_name, bytes) + }} +} +#[macro_export] +macro_rules! kdl_entries_as_i64 { + ( $node:expr ) => { + $node.entries().iter().map(|kdl_node| kdl_node.value().as_i64()) + } +} + +#[macro_export] +macro_rules! kdl_first_entry_as_string { + ( $node:expr ) => { + $node.entries().iter().next().and_then(|s| s.value().as_string()) + } +} + +#[macro_export] +macro_rules! kdl_first_entry_as_i64 { + ( $node:expr ) => { + $node.entries().iter().next().and_then(|i| i.value().as_i64()) + } +} + +#[macro_export] +macro_rules! entry_count { + ( $node:expr ) => {{ + $node.entries().iter().len() + }} +} + +#[macro_export] +macro_rules! parse_kdl_action_char_or_string_arguments { + ( $action_name:expr, $action_arguments:expr ) => {{ + let mut chars_to_write = String::new(); + for kdl_value in $action_arguments.iter() { + match kdl_value.as_string() { + Some(string_value) => chars_to_write.push_str(string_value), + None => { + return Err(format!("Failed to parse action: {}", $action_name)); + } + } + }; + Action::new_from_string($action_name, chars_to_write) + }} +} + +pub fn kdl_string_arguments <'a>(arguments: impl Iterator) -> Result, String> { + let mut args: Vec = vec![]; + for kdl_value in arguments { + match kdl_value.as_string() { + Some(string_value) => args.push(string_value.to_string()), + None => { + return Err(format!("Failed to parse kdl arguments")); + } + } + } + Ok(args) +} + +pub fn kdl_child_string_value_for_entry <'a>(command_metadata: &'a KdlDocument, entry_name: &'a str) -> Option<&'a str> { + command_metadata + .get(entry_name) + .and_then(|cwd| cwd.entries().iter().next()) + .and_then(|cwd_value| cwd_value.value().as_string()) +} + +impl Action { + pub fn new_from_bytes(action_name: &str, bytes: Vec) -> Result { + match action_name { + "Write" => { + Ok(Action::Write(bytes)) + }, + "PaneNameInput" => { + Ok(Action::PaneNameInput(bytes)) + } + "TabNameInput" => { + Ok(Action::TabNameInput(bytes)) + } + "GoToTab" => { + let tab_index = *bytes + .get(0) + .ok_or_else(|| format!("Cannot create action: {} from bytes: {:?}", action_name, bytes))? + as u32; + Ok(Action::GoToTab(tab_index)) + } + _ => Err(format!("Cannot create action: {} from bytes: {:?}", action_name, bytes)), + } + } + pub fn new_from_string(action_name: &str, string: String) -> Result { + match action_name { + "WriteChars" => Ok(Action::WriteChars(string)), + "SwitchToMode" => { + match InputMode::try_from(string.as_str()) { + Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)), + Err(_e) => return Err(format!("Failed to parse SwitchToMode. Unknown InputMode: {}", string)), + } + }, + "Resize" => { + let direction = ResizeDirection::from_str(string.as_str())?; + Ok(Action::Resize(direction)) + } + "MoveFocus" => { + let direction = Direction::from_str(string.as_str())?; + Ok(Action::MoveFocus(direction)) + } + "MoveFocusOrTab" => { + let direction = Direction::from_str(string.as_str())?; + Ok(Action::MoveFocusOrTab(direction)) + } + "MovePane" => { + if string.is_empty() { + return Ok(Action::MovePane(None)); + } else { + let direction = Direction::from_str(string.as_str())?; + Ok(Action::MovePane(Some(direction))) + } + } + "DumpScreen" => { + Ok(Action::DumpScreen(string)) + } + "NewPane" => { + if string.is_empty() { + return Ok(Action::NewPane(None)); + } else { + let direction = Direction::from_str(string.as_str())?; + Ok(Action::NewPane(Some(direction))) + } + } + _ => Err(format!("Cannot create action: '{}' from string: '{:?}'", action_name, string)), + } + } +} + +impl TryFrom<(&str, &KdlDocument)> for PaletteColor { + type Error = String; + fn try_from((color_name, theme_colors): (&str, &KdlDocument)) -> Result { + let color = theme_colors.get(color_name).ok_or(format!("Failed to parse color"))?; + let entry_count = entry_count!(color); + let is_rgb = || entry_count == 3; + let is_three_digit_hex = || { + match kdl_first_entry_as_string!(color) { + // 4 including the '#' character + Some(s) => entry_count == 1 && s.starts_with('#') && s.len() == 4, + None => false + } + }; + let is_six_digit_hex = || { + match kdl_first_entry_as_string!(color) { + // 7 including the '#' character + Some(s) => entry_count == 1 && s.starts_with('#') && s.len() == 7, + None => false, + } + }; + let is_eight_bit = || { + kdl_first_entry_as_i64!(color).is_some() && entry_count == 1 + }; + if is_rgb() { + let mut channels = kdl_entries_as_i64!(color); + let r = channels.next().unwrap().ok_or(format!("invalid color"))? as u8; + let g = channels.next().unwrap().ok_or(format!("invalid_color"))? as u8; + let b = channels.next().unwrap().ok_or(format!("invalid_color"))? as u8; + Ok(PaletteColor::Rgb((r, g, b))) + } else if is_three_digit_hex() { + // eg. #fff (hex, will be converted to rgb) + let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); + s.remove(0); + // TODO: test this + // TODO: why do we need the * 0x11 here? + let r = u8::from_str_radix(&s[0..1], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; + let g = u8::from_str_radix(&s[1..2], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; + let b = u8::from_str_radix(&s[2..3], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; + Ok(PaletteColor::Rgb((r, g, b))) + } else if is_six_digit_hex() { + // eg. #ffffff (hex, will be converted to rgb) + let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); + s.remove(0); + let r = u8::from_str_radix(&s[0..2], 16).map_err(|e| format!("Failed to parse color: {}", e))?; + let g = u8::from_str_radix(&s[2..4], 16).map_err(|e| format!("Failed to parse color: {}", e))?; + let b = u8::from_str_radix(&s[4..6], 16).map_err(|e| format!("Failed to parse color: {}", e))?; + Ok(PaletteColor::Rgb((r, g, b))) + } else if is_eight_bit() { + let n = kdl_first_entry_as_i64!(color).ok_or(format!("Failed to parse color"))?; + Ok(PaletteColor::EightBit(n as u8)) // TODO: test values greater than u8 bounds + } else { + Err(format!("Failed to parse color")) + } + } +} + +impl TryFrom<(&str, Vec<&KdlValue>, Vec<&KdlDocument>)> for Action { + type Error = String; + fn try_from((action_name, action_arguments, action_children): (&str, Vec<&KdlValue>, Vec<&KdlDocument>)) -> Result { + match action_name { + "Quit" => parse_kdl_action_arguments!(action_name, action_arguments), + "FocusNextPane" => parse_kdl_action_arguments!(action_name, action_arguments), + "FocusPreviousPane" => parse_kdl_action_arguments!(action_name, action_arguments), + "SwitchFocus" => parse_kdl_action_arguments!(action_name, action_arguments), + "EditScrollback" => parse_kdl_action_arguments!(action_name, action_arguments), + "ScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments), + "ScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments), + "ScrollToBottom" => parse_kdl_action_arguments!(action_name, action_arguments), + "PageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments), + "PageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments), + "HalfPageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments), + "HalfPageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments), + "ToggleFocusFullscreen" => parse_kdl_action_arguments!(action_name, action_arguments), + "TogglePaneFrames" => parse_kdl_action_arguments!(action_name, action_arguments), + "ToggleActiveSyncTab" => parse_kdl_action_arguments!(action_name, action_arguments), + "TogglePaneEmbedOrFloating" => parse_kdl_action_arguments!(action_name, action_arguments), + "ToggleFloatingPanes" => parse_kdl_action_arguments!(action_name, action_arguments), + "CloseFocus" => parse_kdl_action_arguments!(action_name, action_arguments), + "UndoRenamePane" => parse_kdl_action_arguments!(action_name, action_arguments), + "NoOp" => parse_kdl_action_arguments!(action_name, action_arguments), + "GoToNextTab" => parse_kdl_action_arguments!(action_name, action_arguments), + "GoToPreviousTab" => parse_kdl_action_arguments!(action_name, action_arguments), + "CloseTab" => parse_kdl_action_arguments!(action_name, action_arguments), + "ToggleTab" => parse_kdl_action_arguments!(action_name, action_arguments), + "UndoRenameTab" => parse_kdl_action_arguments!(action_name, action_arguments), + "Detach" => parse_kdl_action_arguments!(action_name, action_arguments), + "Copy" => parse_kdl_action_arguments!(action_name, action_arguments), + "Confirm" => parse_kdl_action_arguments!(action_name, action_arguments), + "Deny" => parse_kdl_action_arguments!(action_name, action_arguments), + "Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "WriteChars" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "SwitchToMode" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "Resize" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "MoveFocus" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "MoveFocusOrTab" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "MovePane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "DumpScreen" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "NewPane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "PaneNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "NewTab" => Ok(Action::NewTab(None)), // TODO: consider the Some(TabLayout) case... + "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "Run" => { + let arguments = action_arguments.iter().copied(); + let mut args = kdl_string_arguments(arguments)?; + if args.is_empty() { + return Err(format!("No command found in Run action")); + } + let command = args.remove(0); + let command_metadata = action_children.iter().next(); + let cwd = command_metadata + .and_then(|c_m| kdl_child_string_value_for_entry(c_m, "cwd")) + .map(|cwd_string| PathBuf::from(cwd_string)); + let direction = command_metadata + .and_then(|c_m| kdl_child_string_value_for_entry(c_m, "direction")) + .and_then(|direction_string| Direction::from_str(direction_string).ok()); + let run_command_action = RunCommandAction { + command: PathBuf::from(command), + args, + cwd, + direction, + }; + Ok(Action::Run(run_command_action)) + } + _ => { + Err(format!("Failed to parse action: {}", action_name)) + } + } + } +} + +impl TryFrom<&KdlValue> for Key { + type Error = String; + fn try_from(kdl_value: &KdlValue) -> Result { + let key_str = kdl_value.as_string(); + if key_str.is_none() { + return Err(format!("Failed to parse key: {}", kdl_value)); + } + let key_str = key_str.unwrap(); + let mut modifier: Option<&str> = None; + let mut main_key: Option<&str> = None; + for (index, part) in key_str.split_ascii_whitespace().enumerate() { + // TODO: handle F(u8) + if index == 0 && (part == "Ctrl" || part == "Alt") { + modifier = Some(part); + } else if main_key.is_none() { + main_key = Some(part) + } + } + match (modifier, main_key) { + (Some("Ctrl"), Some(main_key)) => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Ctrl(key_char)) + } else { + Err(format!("Failed to parse key: {}", key_str)) + } + }, + (Some("Alt"), Some(main_key)) => { + match main_key { + // why crate::data::Direction and not just Direction? + // Because it's a different type that we export in this wasm mandated soup - we + // don't like it either! This will be solved as we chip away at our tech-debt + "Left" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Left))), + "Right" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Right))), + "Up" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Up))), + "Down" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Down))), + _ => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Alt(CharOrArrow::Char(key_char))) + } else { + Err(format!("Failed to parse key: {}", key_str)) + } + } + } + }, + (None, Some(main_key)) => { + match main_key { + "Backspace" => Ok(Key::Backspace), + "Left" => Ok(Key::Left), + "Right" => Ok(Key::Right), + "Up" => Ok(Key::Up), + "Down" => Ok(Key::Down), + "Home" => Ok(Key::Home), + "End" => Ok(Key::End), + "PageUp" => Ok(Key::PageUp), + "PageDown" => Ok(Key::PageDown), + "Tab" => Ok(Key::BackTab), + "Delete" => Ok(Key::Delete), + "Insert" => Ok(Key::Insert), + "Space" => Ok(Key::Char(' ')), + "Enter" => Ok(Key::Char('\n')), + "Esc" => Ok(Key::Esc), + _ => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Char(key_char)) + } else if key_count > 1 { + if let Some(first_char) = key_chars.next() { + if first_char == 'F' { + let f_index: String = key_chars.collect(); + let f_index: u8 = f_index.parse().map_err(|e| format!("Failed to parse F index: {}", e))?; + if f_index >= 1 && f_index <= 12 { + return Ok(Key::F(f_index)); + } + } + } + Err(format!("Failed to parse key: {}", key_str)) + } else { + Err(format!("Failed to parse key: {}", key_str)) + } + } + } + } + _ => Err(format!("Failed to parse key: {}", key_str)) + } + } +} diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs index 94c4f18dbf..732ec7603c 100644 --- a/zellij-utils/src/lib.rs +++ b/zellij-utils/src/lib.rs @@ -1,5 +1,7 @@ pub mod data; +#[cfg(not(target_family = "wasm"))] +pub mod kdl; #[cfg(not(target_family = "wasm"))] pub mod channels; #[cfg(not(target_family = "wasm"))] diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index 9b2c36b233..9fac91b706 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -88,7 +88,7 @@ pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> { pub const DEFAULT_CONFIG: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", - "assets/config/default.yaml" + "assets/config/default.kdl" )); pub const DEFAULT_LAYOUT: &[u8] = include_bytes!(concat!( @@ -256,6 +256,8 @@ impl Setup { ); }; + // unimplemented!() + // Ok((config, None, config_options)) // TODO: no!!!!!111oneoneone Setup::merge_config_with_layout(config, layout, config_options) } @@ -317,7 +319,7 @@ impl Setup { } else { config_options }; - let config = config.merge(layout_config.try_into()?); + // let config = config.merge(layout_config.try_into()?); // TODO: NO! handle this! (config, config_options) } else { (config, config_options) @@ -366,7 +368,8 @@ impl Setup { } if let Some(config_file) = config_file { writeln!(&mut message, "[CONFIG FILE]: {:?}", config_file).unwrap(); - match Config::new(&config_file) { + // match Config::new(&config_file) { + match Config::from_path(&config_file, None) { Ok(_) => message.push_str("[CONFIG FILE]: Well defined.\n"), Err(e) => writeln!(&mut message, "[CONFIG ERROR]: {}", e).unwrap(), } @@ -473,98 +476,101 @@ impl Setup { } } -#[cfg(test)] -mod setup_test { - use super::Setup; - use crate::data::InputMode; - use crate::input::{ - config::{Config, ConfigError}, - layout::LayoutFromYamlIntermediate, - options::Options, - }; - - fn deserialise_config_and_layout( - config: &str, - layout: &str, - ) -> Result<(Config, LayoutFromYamlIntermediate), ConfigError> { - let config = Config::from_yaml(config)?; - let layout = LayoutFromYamlIntermediate::from_yaml(layout)?; - Ok((config, layout)) - } - - #[test] - fn empty_config_empty_layout() { - let goal = Config::default(); - let config = r""; - let layout = r""; - let config_layout_result = deserialise_config_and_layout(config, layout); - let (config, layout) = config_layout_result.unwrap(); - let config_options = Options::default(); - let (config, _layout, _config_options) = - Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); - assert_eq!(config, goal); - } - - #[test] - fn config_empty_layout() { - let mut goal = Config::default(); - goal.options.default_shell = Some(std::path::PathBuf::from("fish")); - let config = r"--- - default_shell: fish"; - let layout = r""; - let config_layout_result = deserialise_config_and_layout(config, layout); - let (config, layout) = config_layout_result.unwrap(); - let config_options = Options::default(); - let (config, _layout, _config_options) = - Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); - assert_eq!(config, goal); - } - - #[test] - fn layout_overwrites_config() { - let mut goal = Config::default(); - goal.options.default_shell = Some(std::path::PathBuf::from("bash")); - let config = r"--- - default_shell: fish"; - let layout = r"--- - default_shell: bash"; - let config_layout_result = deserialise_config_and_layout(config, layout); - let (config, layout) = config_layout_result.unwrap(); - let config_options = Options::default(); - let (config, _layout, _config_options) = - Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); - assert_eq!(config, goal); - } - - #[test] - fn empty_config_nonempty_layout() { - let mut goal = Config::default(); - goal.options.default_shell = Some(std::path::PathBuf::from("bash")); - let config = r""; - let layout = r"--- - default_shell: bash"; - let config_layout_result = deserialise_config_and_layout(config, layout); - let (config, layout) = config_layout_result.unwrap(); - let config_options = Options::default(); - let (config, _layout, _config_options) = - Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); - assert_eq!(config, goal); - } - - #[test] - fn nonempty_config_nonempty_layout() { - let mut goal = Config::default(); - goal.options.default_shell = Some(std::path::PathBuf::from("bash")); - goal.options.default_mode = Some(InputMode::Locked); - let config = r"--- - default_mode: locked"; - let layout = r"--- - default_shell: bash"; - let config_layout_result = deserialise_config_and_layout(config, layout); - let (config, layout) = config_layout_result.unwrap(); - let config_options = Options::default(); - let (config, _layout, _config_options) = - Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); - assert_eq!(config, goal); - } -} +// TODO: write these test cases once we're done with the layout +// +// #[cfg(test)] +// mod setup_test { +// use super::Setup; +// use crate::data::InputMode; +// use crate::input::{ +// config::{Config, ConfigError}, +// layout::LayoutFromYamlIntermediate, +// options::Options, +// }; +// +// fn deserialise_config_and_layout( +// config: &str, +// layout: &str, +// ) -> Result<(Config, LayoutFromYamlIntermediate), ConfigError> { +// // let config = Config::from_yaml(config)?; +// let config = Config::from_kdl(config)?; +// let layout = LayoutFromYamlIntermediate::from_yaml(layout)?; +// Ok((config, layout)) +// } +// +// #[test] +// fn empty_config_empty_layout() { +// let goal = Config::default(); +// let config = r""; +// let layout = r""; +// let config_layout_result = deserialise_config_and_layout(config, layout); +// let (config, layout) = config_layout_result.unwrap(); +// let config_options = Options::default(); +// let (config, _layout, _config_options) = +// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); +// assert_eq!(config, goal); +// } +// +// #[test] +// fn config_empty_layout() { +// let mut goal = Config::default(); +// goal.options.default_shell = Some(std::path::PathBuf::from("fish")); +// let config = r"--- +// default_shell: fish"; +// let layout = r""; +// let config_layout_result = deserialise_config_and_layout(config, layout); +// let (config, layout) = config_layout_result.unwrap(); +// let config_options = Options::default(); +// let (config, _layout, _config_options) = +// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); +// assert_eq!(config, goal); +// } +// +// #[test] +// fn layout_overwrites_config() { +// let mut goal = Config::default(); +// goal.options.default_shell = Some(std::path::PathBuf::from("bash")); +// let config = r"--- +// default_shell: fish"; +// let layout = r"--- +// default_shell: bash"; +// let config_layout_result = deserialise_config_and_layout(config, layout); +// let (config, layout) = config_layout_result.unwrap(); +// let config_options = Options::default(); +// let (config, _layout, _config_options) = +// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); +// assert_eq!(config, goal); +// } +// +// #[test] +// fn empty_config_nonempty_layout() { +// let mut goal = Config::default(); +// goal.options.default_shell = Some(std::path::PathBuf::from("bash")); +// let config = r""; +// let layout = r"--- +// default_shell: bash"; +// let config_layout_result = deserialise_config_and_layout(config, layout); +// let (config, layout) = config_layout_result.unwrap(); +// let config_options = Options::default(); +// let (config, _layout, _config_options) = +// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); +// assert_eq!(config, goal); +// } +// +// #[test] +// fn nonempty_config_nonempty_layout() { +// let mut goal = Config::default(); +// goal.options.default_shell = Some(std::path::PathBuf::from("bash")); +// goal.options.default_mode = Some(InputMode::Locked); +// let config = r"--- +// default_mode: locked"; +// let layout = r"--- +// default_shell: bash"; +// let config_layout_result = deserialise_config_and_layout(config, layout); +// let (config, layout) = config_layout_result.unwrap(); +// let config_options = Options::default(); +// let (config, _layout, _config_options) = +// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); +// assert_eq!(config, goal); +// } +// } From 179adaf6539eca7f5b5b79d0ee80cb96695f2598 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sun, 7 Aug 2022 12:25:36 +0200 Subject: [PATCH 03/55] work --- zellij-utils/src/input/config.rs | 40 ++++++++++++++---------------- zellij-utils/src/input/keybinds.rs | 3 +++ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 927a45ff6d..7276cbd06f 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -18,7 +18,7 @@ use kdl::{KdlDocument, KdlValue, KdlNode}; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; -use super::keybinds::{Keybinds, KeybindsFromYaml}; +use super::keybinds::{KeybindsFromYaml}; use super::options::{Options, OnForceClose, Clipboard}; use super::plugins::{PluginsConfig, PluginsConfigError, PluginsConfigFromYaml, PluginConfig, PluginType}; use super::theme::{ThemesFromYaml, UiConfig, Theme, FrameConfig}; @@ -185,9 +185,9 @@ impl Config { // } // } /// Uses defaults, but lets config override them. - pub fn parse_keybindings(kdl_keybinds: &KdlNode, keybindings_to_override: Option>>>) -> Result>>, String> { + pub fn parse_keybindings(kdl_keybinds: &KdlNode, keybindings_to_override: HashMap>>) -> Result>>, String> { let clear_defaults = kdl_keybinds.get("clear-defaults").and_then(|c| c.value().as_bool()).unwrap_or(false) == true; - let mut keybinds_from_config: HashMap>> = if clear_defaults {HashMap::new()} else {keybindings_to_override.unwrap_or_else(|| HashMap::new())}; + let mut keybinds_from_config: HashMap>> = if clear_defaults {HashMap::new()} else {keybindings_to_override}; for mode in kdl_keybinds.children().unwrap().nodes() { let mode_name = mode.name().value(); if mode_name == "unbind" { @@ -230,7 +230,6 @@ impl Config { } } } - // keybinds_from_config.insert(input_mode, input_mode_keybinds); } if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { let keys: Vec = global_unbind.entries().iter().map(|key_shortcut| { @@ -403,40 +402,39 @@ impl Config { Ok(env) } pub fn from_kdl(kdl_config: &str, base_config: Option) -> ConfigResult { - // TODO: CONTINUE HERE - // - adapt the existing tests to work with the new way - // - then write new tests - // - then refactor - // - then move on to layouts and everything else (what?) let mut config = base_config.unwrap_or_else(|| Config::default()); let kdl_config: KdlDocument = kdl_config.parse()?; if let Some(keybinds) = kdl_config.get("keybinds") { - let keybinds_from_config = Config::parse_keybindings(&keybinds, Some(config.keybinds)).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse keybindings: {:?}", e)))?; - config.keybinds = keybinds_from_config; + config.keybinds = Config::parse_keybindings(&keybinds, config.keybinds) + .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse keybindings: {:?}", e)))?; } - let options = Config::parse_options(&kdl_config); - config.options = options; + config.options = Config::parse_options(&kdl_config); if let Some(kdl_themes) = kdl_config.get("themes") { - let themes = Config::parse_themes(&kdl_themes).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse themes: {:?}", e)))?; - config.themes = Some(themes); + config.themes = Some(Config::parse_themes(&kdl_themes).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse themes: {:?}", e)))?); } if let Some(kdl_plugin_config) = kdl_config.get("plugins") { - let plugins = Config::parse_plugins(&kdl_plugin_config).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse plugins: {:?}", e)))?; - config.plugins = PluginsConfig::from_data(plugins); + config.plugins = PluginsConfig::from_data( + Config::parse_plugins(&kdl_plugin_config) + .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse plugins: {:?}", e)))? + ); } if let Some(kdl_ui_config) = kdl_config.get("ui") { - let ui_config = Config::parse_ui_config(&kdl_ui_config).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse ui config: {:?}", e)))?; - config.ui = Some(ui_config); + config.ui = Some( + Config::parse_ui_config(&kdl_ui_config) + .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse ui config: {:?}", e)))? + ); } if let Some(env_config) = kdl_config.get("env") { - let env = Config::parse_env_variables_config(&env_config).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse env variable config: {:?}", e)))?; - config.env = EnvironmentVariables::from_data(env); + config.env = EnvironmentVariables::from_data( + Config::parse_env_variables_config(&env_config) + .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse env variable config: {:?}", e)))? + ); } Ok(config) diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index 4730d835b5..bdf7528f45 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -189,6 +189,9 @@ impl Keybinds { /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current /// [`InputMode`] and [`Keybinds`]. pub fn key_to_actions( + // TODO CONTINUE HERE (06/08): remove everything except this function and see if it still compiles, + // if so, convert this into the new way and add a fn from_kdl to it that will be the + // parse_keybindings function that's in the config object now key: &Key, raw_bytes: Vec, mode: &InputMode, From 9d882824984f2d2bcf1a092fa1864d62676db1c6 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 8 Aug 2022 12:47:07 +0200 Subject: [PATCH 04/55] refactor(config): move stuff around --- zellij-client/src/input_handler.rs | 2 +- zellij-client/src/lib.rs | 2 +- .../assets/config/test_config_deleteme.kdl | 2 +- zellij-utils/src/data.rs | 116 ++- zellij-utils/src/envs.rs | 16 + zellij-utils/src/input/config.rs | 675 +++++------------- zellij-utils/src/input/keybinds.rs | 256 +++---- zellij-utils/src/input/options.rs | 67 ++ zellij-utils/src/input/plugins.rs | 25 +- zellij-utils/src/input/theme.rs | 55 +- zellij-utils/src/kdl.rs | 218 +++++- 11 files changed, 734 insertions(+), 700 deletions(-) diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs index 7428bac0f6..bab0cd33e5 100644 --- a/zellij-client/src/input_handler.rs +++ b/zellij-client/src/input_handler.rs @@ -120,7 +120,7 @@ impl InputHandler { } fn handle_key(&mut self, key: &Key, raw_bytes: Vec) { let keybinds = &self.config.keybinds; - for action in Keybinds::key_to_actions(key, raw_bytes, &self.mode, keybinds) { + for action in keybinds.get_actions_for_key_in_mode_or_default_action(&self.mode, key, raw_bytes) { let should_exit = self.dispatch_action(action, None); if should_exit { self.should_exit = true; diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index da521edab5..a83e163e68 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -160,7 +160,7 @@ pub fn start_client( size: full_screen_ws, style: Style { colors: palette, - rounded_corners: config.ui.unwrap_or_default().pane_frames.rounded_corners, + rounded_corners: config.ui.pane_frames.rounded_corners, }, }; diff --git a/zellij-utils/assets/config/test_config_deleteme.kdl b/zellij-utils/assets/config/test_config_deleteme.kdl index 7b8c8d86b3..25d8403750 100644 --- a/zellij-utils/assets/config/test_config_deleteme.kdl +++ b/zellij-utils/assets/config/test_config_deleteme.kdl @@ -1,7 +1,7 @@ keybinds { unbind "Ctrl g"; normal clear-defaults=true { - // unbind "Alt ="; // TODO: removeme + unbind "Alt ="; // TODO: removeme bind "Ctrl g" { SwitchToMode "Locked"; } bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl n" { SwitchToMode "Resize"; } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index a8060bc979..b32e64e64e 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -52,6 +52,96 @@ pub enum Key { Esc, } +impl FromStr for Key { + type Err = Box; + fn from_str(key_str: &str) -> Result { + let mut modifier: Option<&str> = None; + let mut main_key: Option<&str> = None; + for (index, part) in key_str.split_ascii_whitespace().enumerate() { + // TODO: handle F(u8) + if index == 0 && (part == "Ctrl" || part == "Alt") { + modifier = Some(part); + } else if main_key.is_none() { + main_key = Some(part) + } + } + match (modifier, main_key) { + (Some("Ctrl"), Some(main_key)) => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Ctrl(key_char)) + } else { + Err(format!("Failed to parse key: {}", key_str).into()) + } + }, + (Some("Alt"), Some(main_key)) => { + match main_key { + // why crate::data::Direction and not just Direction? + // Because it's a different type that we export in this wasm mandated soup - we + // don't like it either! This will be solved as we chip away at our tech-debt + "Left" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Left))), + "Right" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Right))), + "Up" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Up))), + "Down" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Down))), + _ => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Alt(CharOrArrow::Char(key_char))) + } else { + Err(format!("Failed to parse key: {}", key_str).into()) + } + } + } + }, + (None, Some(main_key)) => { + match main_key { + "Backspace" => Ok(Key::Backspace), + "Left" => Ok(Key::Left), + "Right" => Ok(Key::Right), + "Up" => Ok(Key::Up), + "Down" => Ok(Key::Down), + "Home" => Ok(Key::Home), + "End" => Ok(Key::End), + "PageUp" => Ok(Key::PageUp), + "PageDown" => Ok(Key::PageDown), + "Tab" => Ok(Key::BackTab), + "Delete" => Ok(Key::Delete), + "Insert" => Ok(Key::Insert), + "Space" => Ok(Key::Char(' ')), + "Enter" => Ok(Key::Char('\n')), + "Esc" => Ok(Key::Esc), + _ => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Char(key_char)) + } else if key_count > 1 { + if let Some(first_char) = key_chars.next() { + if first_char == 'F' { + let f_index: String = key_chars.collect(); + let f_index: u8 = f_index.parse().map_err(|e| format!("Failed to parse F index: {}", e))?; + if f_index >= 1 && f_index <= 12 { + return Ok(Key::F(f_index)); + } + } + } + Err(format!("Failed to parse key: {}", key_str).into()) + } else { + Err(format!("Failed to parse key: {}", key_str).into()) + } + } + } + } + _ => Err(format!("Failed to parse key: {}", key_str).into()) + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(untagged)] pub enum CharOrArrow { @@ -194,19 +284,19 @@ impl FromStr for InputMode { fn from_str(s: &str) -> Result { match s { - "normal" => Ok(InputMode::Normal), - "resize" => Ok(InputMode::Resize), - "locked" => Ok(InputMode::Locked), - "pane" => Ok(InputMode::Pane), - "tab" => Ok(InputMode::Tab), - "scroll" => Ok(InputMode::Scroll), - "renametab" => Ok(InputMode::RenameTab), - "session" => Ok(InputMode::Session), - "move" => Ok(InputMode::Move), - "tmux" => Ok(InputMode::Tmux), - "prompt" => Ok(InputMode::Prompt), - "renamepane" => Ok(InputMode::RenamePane), - e => Err(e.to_string().into()), + "normal" | "Normal" => Ok(InputMode::Normal), + "locked" | "Locked" => Ok(InputMode::Locked), + "resize" | "Resize" => Ok(InputMode::Resize), + "pane" | "Pane" => Ok(InputMode::Pane), + "tab" | "Tab" => Ok(InputMode::Tab), + "scroll" | "Scroll" => Ok(InputMode::Scroll), + "renametab" | "RenameTab" => Ok(InputMode::RenameTab), + "renamepane" | "RenamePane" => Ok(InputMode::RenamePane), + "session" | "Session" => Ok(InputMode::Session), + "move" | "Move" => Ok(InputMode::Move), + "prompt" | "Prompt" => Ok(InputMode::Prompt), + "tmux" | "Tmux" => Ok(InputMode::Tmux), + e => Err(format!("unknown mode \"{}\"", e).into()), } } } diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index 80a34a4aba..52ab637fa7 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -1,6 +1,9 @@ /// Uniformly operates ZELLIJ* environment variables use anyhow::Result; use serde::{Deserialize, Serialize}; +use crate::input::config::ConfigError; +use kdl::KdlNode; +use crate::{kdl_children_or_error, kdl_children_nodes_or_error, kdl_name, kdl_first_entry_as_string, kdl_first_entry_as_i64}; use std::{ collections::HashMap, env::{set_var, var}, @@ -51,6 +54,19 @@ impl EnvironmentVariables { env: data } } + pub fn from_kdl(kdl_env_variables: &KdlNode) -> Result { + let mut env: HashMap = HashMap::new(); + for env_var in kdl_children_nodes_or_error!(kdl_env_variables, "empty env variable block") { + let env_var_name = kdl_name!(env_var); + let env_var_str_value = kdl_first_entry_as_string!(env_var).map(|s| format!("{}", s.to_string())); + let env_var_int_value = kdl_first_entry_as_i64!(env_var).map(|s| format!("{}", s.to_string())); + let env_var_value = env_var_str_value + .or(env_var_int_value) + .ok_or::>(format!("Failed to parse env var: {:?}", env_var_name).into())?; + env.insert(env_var_name.into(), env_var_value); + } + Ok(EnvironmentVariables::from_data(env)) + } /// Set all the ENVIRONMENT VARIABLES, that are configured /// in the configuration and layout files diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 7276cbd06f..a163e5376b 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -18,10 +18,10 @@ use kdl::{KdlDocument, KdlValue, KdlNode}; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; -use super::keybinds::{KeybindsFromYaml}; +use super::keybinds::{Keybinds, KeybindsFromYaml}; use super::options::{Options, OnForceClose, Clipboard}; use super::plugins::{PluginsConfig, PluginsConfigError, PluginsConfigFromYaml, PluginConfig, PluginType}; -use super::theme::{ThemesFromYaml, UiConfig, Theme, FrameConfig}; +use super::theme::{ThemesFromYaml, UiConfig, Theme, Themes, FrameConfig}; use crate::cli::{CliArgs, Command}; use crate::envs::EnvironmentVariables; use crate::setup; @@ -47,25 +47,15 @@ pub struct ConfigFromYaml { } /// Main configuration. -#[derive(Debug, Clone, PartialEq, Deserialize, knuffel::Decode)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct Config { - pub keybinds: HashMap>>, // TODO: make this a type + pub keybinds: Keybinds, pub options: Options, - pub themes: Option>, // TODO: typify? + pub themes: Themes, pub plugins: PluginsConfig, - // pub ui: Option, - pub ui: Option, + pub ui: UiConfig, pub env: EnvironmentVariables, } -// #[derive(Debug, Clone, PartialEq, Deserialize)] -// pub struct Config { -// pub keybinds: Keybinds, -// pub options: Options, -// pub themes: Option, -// pub plugins: PluginsConfig, -// pub ui: Option, -// pub env: EnvironmentVariablesFromYaml, -// } #[derive(Error, Debug)] pub enum ConfigError { @@ -80,6 +70,8 @@ pub enum ConfigError { // Io error #[error("IoError: {0}")] Io(#[from] io::Error), + #[error("Config error: {0}")] + Std(#[from] Box), // Io error with path context #[error("IoError: {0}, File: {1}")] IoPath(io::Error, PathBuf), @@ -94,27 +86,6 @@ pub enum ConfigError { PluginsError(#[from] PluginsConfigError), } -impl Default for Config { - fn default() -> Self { - // let keybinds = Keybinds::default(); - let keybinds = HashMap::new(); - let options = Options::default(); - let themes = None; - let env = EnvironmentVariables::default(); - let plugins = PluginsConfig::default(); - let ui = None; - - Config { - keybinds, - options, - themes, - plugins, - env, - ui, - } - } -} - impl TryFrom<&CliArgs> for Config { type Error = ConfigError; @@ -154,310 +125,32 @@ impl TryFrom<&CliArgs> for Config { impl Config { pub fn theme_config(&self, opts: &Options) -> Option { match &opts.theme { - // Some(theme) => from_yaml.from_default_theme(theme.to_owned()), - Some(theme_name) => self.themes.as_ref() - .and_then(|themes| themes.get(theme_name)) - .map(|theme| theme.palette), - // None => from_yaml.from_default_theme("default".into()), - None => self.themes.as_ref() - .and_then(|themes| themes.get("default")) - .map(|theme| theme.palette) - } - } - /// Uses defaults, but lets config override them. -// pub fn from_yaml(yaml_config: &str) -> ConfigResult { -// let maybe_config_from_yaml: Option = match serde_yaml::from_str(yaml_config) -// { -// Err(e) => { -// // needs direct check, as `[ErrorImpl]` is private -// // https://github.com/dtolnay/serde-yaml/issues/121 -// if yaml_config.is_empty() { -// return Ok(Config::default()); -// } -// return Err(ConfigError::Serde(e)); -// }, -// Ok(config) => config, -// }; -// -// match maybe_config_from_yaml { -// None => Ok(Config::default()), -// Some(config) => config.try_into(), -// } -// } - /// Uses defaults, but lets config override them. - pub fn parse_keybindings(kdl_keybinds: &KdlNode, keybindings_to_override: HashMap>>) -> Result>>, String> { - let clear_defaults = kdl_keybinds.get("clear-defaults").and_then(|c| c.value().as_bool()).unwrap_or(false) == true; - let mut keybinds_from_config: HashMap>> = if clear_defaults {HashMap::new()} else {keybindings_to_override}; - for mode in kdl_keybinds.children().unwrap().nodes() { - let mode_name = mode.name().value(); - if mode_name == "unbind" { - continue; - } - let input_mode = InputMode::try_from(mode_name).unwrap(); - let input_mode_keybinds = keybinds_from_config.entry(input_mode).or_insert_with(HashMap::new); - let clear_defaults_for_mode = mode.get("clear-defaults").is_some(); - if clear_defaults_for_mode { - input_mode_keybinds.clear(); - } - for key_block in mode.children().unwrap().nodes() { - let key_block_name = key_block.name().value(); - if key_block_name == "bind" { - let keys: Vec = key_block.entries().iter().map(|key_shortcut| { - let key_shortcut = key_shortcut.value(); - Key::try_from(key_shortcut).unwrap() - }).collect(); - let actions: Vec = key_block.children().unwrap().nodes().iter().map(|action| { - let action_name = action.name().value(); - let action_arguments: Vec<&KdlValue> = action.entries().iter().map(|arg| arg.value()).collect(); - let action_children: Vec<&KdlDocument> = action.children().iter().copied().collect(); - Action::try_from((action_name, action_arguments, action_children)).unwrap() - }).collect(); - for key in keys { - input_mode_keybinds.insert(key, actions.clone()); - } - } - } - for key_block in mode.children().unwrap().nodes() { - // we loop twice so that the unbinds always happen after the binds - let key_block_name = key_block.name().value(); - if key_block_name == "unbind" { - let keys: Vec = key_block.entries().iter().map(|key_shortcut| { - let key_shortcut = key_shortcut.value(); - Key::try_from(key_shortcut).unwrap() - }).collect(); - for key in keys { - input_mode_keybinds.remove(&key); - } - } - } - } - if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { - let keys: Vec = global_unbind.entries().iter().map(|key_shortcut| { - let key_shortcut = key_shortcut.value(); - Key::try_from(key_shortcut).unwrap() - }).collect(); - for mode in keybinds_from_config.values_mut() { - for key in &keys { - mode.remove(&key); - } - } - }; - Ok(keybinds_from_config) - } - fn parse_options(kdl_config: &KdlDocument) -> Options { - // parse options - let on_force_close = kdl_config.get("on_force_close") - .and_then(|on_force_close| on_force_close.entries().iter().next()) - .and_then(|on_force_close| on_force_close.value().as_string()) - .and_then(|on_force_close| OnForceClose::from_str(on_force_close).ok()); - let simplified_ui = kdl_config.get("simplified_ui") - .and_then(|simplified_ui| simplified_ui.entries().iter().next()) - .and_then(|simplified_ui| simplified_ui.value().as_bool()); - let default_shell = kdl_config.get("default_shell") - .and_then(|default_shell| default_shell.entries().iter().next()) - .and_then(|default_shell| default_shell.value().as_string()) - .map(|default_shell| PathBuf::from(default_shell)); - let pane_frames = kdl_config.get("pane_frames") - .and_then(|pane_frames| pane_frames.entries().iter().next()) - .and_then(|pane_frames| pane_frames.value().as_bool()); - let theme = kdl_config.get("theme") - .and_then(|theme| theme.entries().iter().next()) - .and_then(|theme| theme.value().as_string()) - .map(|theme| theme.to_string()); - let default_mode = kdl_config.get("default_mode") - .and_then(|default_mode| default_mode.entries().iter().next()) - .and_then(|default_mode| default_mode.value().as_string()) - .and_then(|default_mode| InputMode::try_from(default_mode).ok()); - let mouse_mode = kdl_config.get("mouse_mode") - .and_then(|mouse_mode| mouse_mode.entries().iter().next()) - .and_then(|mouse_mode| mouse_mode.value().as_bool()); - let scroll_buffer_size = kdl_config.get("scroll_buffer_size") - .and_then(|scroll_buffer_size| scroll_buffer_size.entries().iter().next()) - .and_then(|scroll_buffer_size| scroll_buffer_size.value().as_i64()) - .map(|scroll_buffer_size| scroll_buffer_size as usize); - let copy_command = kdl_config.get("copy_command") - .and_then(|copy_command| copy_command.entries().iter().next()) - .and_then(|copy_command| copy_command.value().as_string()) - .map(|copy_command| copy_command.to_string()); - let copy_clipboard = kdl_config.get("copy_clipboard") - .and_then(|copy_clipboard| copy_clipboard.entries().iter().next()) - .and_then(|copy_clipboard| copy_clipboard.value().as_string()) - .and_then(|copy_clipboard| Clipboard::from_str(copy_clipboard).ok()); - let copy_on_select = kdl_config.get("copy_on_select") - .and_then(|copy_on_select| copy_on_select.entries().iter().next()) - .and_then(|copy_on_select| copy_on_select.value().as_bool()); - let scrollback_editor = kdl_config.get("scrollback_editor") - .and_then(|scrollback_editor| scrollback_editor.entries().iter().next()) - .and_then(|scrollback_editor| scrollback_editor.value().as_string()) - .map(|scrollback_editor| PathBuf::from(scrollback_editor)); - let mirror_session = kdl_config.get("mirror_session") - .and_then(|mirror_session| mirror_session.entries().iter().next()) - .and_then(|mirror_session| mirror_session.value().as_bool()); - Options { - simplified_ui, - theme, - default_mode, - default_shell, - default_layout: Some(PathBuf::from("default")), // TODO - layout_dir: None, // TODO - mouse_mode, - pane_frames, - mirror_session, - on_force_close, - scroll_buffer_size, - copy_command, - copy_clipboard, - copy_on_select, - scrollback_editor, - } - } - pub fn parse_themes(themes_from_kdl: &KdlNode) -> Result, String> { - let mut themes: HashMap = HashMap::new(); - for theme_config in themes_from_kdl.children().unwrap().nodes() { // TODO: no unwraps here or anywhere - let theme_name = theme_config.name().value(); - let theme_colors = theme_config.children().unwrap(); - let theme = Theme { - palette: Palette { - fg: PaletteColor::try_from(("fg", theme_colors))?, - bg: PaletteColor::try_from(("bg", theme_colors))?, - red: PaletteColor::try_from(("red", theme_colors))?, - green: PaletteColor::try_from(("green", theme_colors))?, - yellow: PaletteColor::try_from(("yellow", theme_colors))?, - blue: PaletteColor::try_from(("blue", theme_colors))?, - magenta: PaletteColor::try_from(("magenta", theme_colors))?, - orange: PaletteColor::try_from(("orange", theme_colors))?, - cyan: PaletteColor::try_from(("cyan", theme_colors))?, - black: PaletteColor::try_from(("black", theme_colors))?, - white: PaletteColor::try_from(("white", theme_colors))?, - ..Default::default() - } - }; - themes.insert(theme_name.into(), theme); - } - Ok(themes) - } - pub fn parse_plugins(kdl_plugin_config: &KdlNode) -> Result, String> { - let mut plugins: HashMap = HashMap::new(); - for plugin_config in kdl_plugin_config.children().unwrap().nodes() { // TODO: no unwraps here or anywhere - let plugin_name = String::from(plugin_config.name().value()); - let plugin_tag = PluginTag::new(&plugin_name); - let path = plugin_config.children().unwrap() - .get("path") - .ok_or("Plugin path not found")? - .entries() - .iter() - .next() - .ok_or("Plugin path not found")? - .value() - .as_string() - .ok_or("Invalid plugin path")?; - let path = PathBuf::from(path); - // let allow_exec_host_cmd = plugin_config.children().unwrap().get("_allow_exec_host_cmd").and_then(|a| a.value().as_bool()).unwrap_or(false); - let allow_exec_host_cmd = plugin_config.children().unwrap() - .get("_allow_exec_host_cmd") - .and_then(|a| a.entries().iter().next()) - .and_then(|a| a.value().as_bool()) - .unwrap_or(false); - let plugin_config = PluginConfig { - path, - run: PluginType::Pane(None), - location: RunPluginLocation::Zellij(plugin_tag.clone()), - _allow_exec_host_cmd: allow_exec_host_cmd, - }; - plugins.insert(plugin_tag, plugin_config); - } - Ok(plugins) - } - pub fn parse_ui_config(kdl_ui_config: &KdlNode) -> Result { - let mut ui_config = UiConfig::default(); - if let Some(pane_frames) = kdl_ui_config.children().unwrap().get("pane_frames") { - let rounded_corners = pane_frames.children().unwrap().get("rounded_corners").unwrap().entries().iter().next().unwrap().value().as_bool().unwrap_or(false); - let frame_config = FrameConfig { rounded_corners }; - ui_config.pane_frames = frame_config; + Some(theme_name) => self.themes.get_theme(theme_name).map(|theme| theme.palette), + None => self.themes.get_theme("default").map(|theme| theme.palette) } - Ok(ui_config) - } - pub fn parse_env_variables_config(kdl_env_variables: &KdlNode) -> Result, String> { - let mut env: HashMap = HashMap::new(); - for env_var in kdl_env_variables.children().unwrap().nodes() { // TODO: no unwraps here or anywhere - let env_var_name = String::from(env_var.name().value()); - let env_var_value = env_var - .entries() - .iter() - .next() - .ok_or("environment variable must have a value")?; - let env_var_str_value = env_var_value - .value() - .as_string() - .map(|s| s.to_string()); - let env_var_int_value = env_var_value - .value() - .as_i64() - .map(|s| format!("{}", s.to_string())); - let env_var_value = env_var_str_value - .or(env_var_int_value) - .ok_or(format!("Failed to parse env var value: {:?}", env_var_value))?; - env.insert(env_var_name, env_var_value); - } - Ok(env) } pub fn from_kdl(kdl_config: &str, base_config: Option) -> ConfigResult { let mut config = base_config.unwrap_or_else(|| Config::default()); let kdl_config: KdlDocument = kdl_config.parse()?; - - if let Some(keybinds) = kdl_config.get("keybinds") { - config.keybinds = Config::parse_keybindings(&keybinds, config.keybinds) - .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse keybindings: {:?}", e)))?; + if let Some(kdl_keybinds) = kdl_config.get("keybinds") { + config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds)?; } - - config.options = Config::parse_options(&kdl_config); - + config.options = Options::from_kdl(&kdl_config); if let Some(kdl_themes) = kdl_config.get("themes") { - config.themes = Some(Config::parse_themes(&kdl_themes).map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse themes: {:?}", e)))?); + config.themes = Themes::from_kdl(kdl_themes)?; } - if let Some(kdl_plugin_config) = kdl_config.get("plugins") { - config.plugins = PluginsConfig::from_data( - Config::parse_plugins(&kdl_plugin_config) - .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse plugins: {:?}", e)))? - ); + config.plugins = PluginsConfig::from_kdl(kdl_plugin_config)?; } - if let Some(kdl_ui_config) = kdl_config.get("ui") { - config.ui = Some( - Config::parse_ui_config(&kdl_ui_config) - .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse ui config: {:?}", e)))? - ); + config.ui = UiConfig::from_kdl(&kdl_ui_config)?; } - if let Some(env_config) = kdl_config.get("env") { - config.env = EnvironmentVariables::from_data( - Config::parse_env_variables_config(&env_config) - .map_err(|e| ConfigError::KdlParsingError(format!("Failed to parse env variable config: {:?}", e)))? - ); + config.env = EnvironmentVariables::from_kdl(&env_config)?; } - Ok(config) } - /// Deserializes from given path. -// pub fn new(path: &Path) -> ConfigResult { -// match File::open(path) { -// Ok(mut file) => { -// let mut kdl_config = String::new(); -// file.read_to_string(&mut kdl_config) -// .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; -// Ok(Config::from_kdl(&kdl_config)?) -// -// // let mut yaml_config = String::new(); -// // file.read_to_string(&mut yaml_config) -// // .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; -// // Ok(Config::from_yaml(&yaml_config)?) -// }, -// Err(e) => Err(ConfigError::IoPath(e, path.into())), -// } -// } - /// Gets default configuration from assets // TODO Deserialize the Config from bytes &[u8], // once serde-yaml supports zero-copy @@ -473,53 +166,12 @@ impl Config { file.read_to_string(&mut kdl_config) .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; Config::from_kdl(&kdl_config, default_config) - -// let mut yaml_config = String::new(); -// file.read_to_string(&mut yaml_config) -// .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; -// Ok(Config::from_yaml(&yaml_config)?) }, Err(e) => Err(ConfigError::IoPath(e, path.into())), } } - - /// Merges two Config structs into one Config struct - /// `other` overrides `self`. - pub fn merge(&self, other: Self) -> Self { - Self { - // TODO: merge keybinds in a way that preserves "unbind" attribute - keybinds: self.keybinds.clone(), - options: self.options.merge(other.options), - themes: self.themes.clone(), // TODO - env: self.env.merge(other.env), - plugins: self.plugins.merge(other.plugins), - ui: self.ui, // TODO - } - } } -// impl TryFrom for Config { -// type Error = ConfigError; -// -// fn try_from(config_from_yaml: ConfigFromYaml) -> ConfigResult { -// // let keybinds = Keybinds::get_default_keybinds_with_config(config_from_yaml.keybinds); -// let keybinds = HashMap::new(); -// let options = Options::from_yaml(config_from_yaml.options); -// let themes = config_from_yaml.themes; -// let env = config_from_yaml.env.unwrap_or_default(); -// let plugins = PluginsConfig::get_plugins_with_default(config_from_yaml.plugins.try_into()?); -// let ui = config_from_yaml.ui; -// Ok(Self { -// keybinds, -// options, -// plugins, -// themes, -// env, -// ui, -// }) -// } -// } - // TODO: Split errors up into separate modules #[derive(Debug, Clone)] pub struct LayoutNameInTabError; @@ -617,8 +269,6 @@ mod config_test { assert_eq!(result.unwrap(), Config::from_default_assets().unwrap()); } - // TODO: CONTINUE HERE (04/08) - write these test cases, then refactor - #[test] fn can_define_keybindings_in_configfile() { let config_contents = r#" @@ -629,9 +279,9 @@ mod config_test { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let ctrl_g_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding successfully defined in config"); } @@ -645,15 +295,17 @@ mod config_test { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let alt_h_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| - normal_mode_keybindings.get(&Key::Alt(CharOrArrow::Direction(data::Direction::Left))) - ); - let alt_left_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| - normal_mode_keybindings.get(&Key::Alt(CharOrArrow::Char('h'))) + let alt_h_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Alt(CharOrArrow::Direction(data::Direction::Left)) + ); + let alt_left_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Alt(CharOrArrow::Char('h')) ); assert_eq!(alt_h_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "First keybinding successfully defined in config"); assert_eq!(alt_left_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "Second keybinding successfully defined in config"); @@ -669,11 +321,12 @@ mod config_test { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Action series successfully defined"); } @@ -688,11 +341,12 @@ mod config_test { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Second keybinding was applied"); } @@ -707,16 +361,18 @@ mod config_test { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); - let uppercase_z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('Z')) - ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let uppercase_z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('Z'), + ); assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Lowercase z successfully bound"); assert_eq!(uppercase_z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Uppercase z successfully bound"); } @@ -739,11 +395,12 @@ mod config_test { "#; let default_config = Config::from_kdl(default_config_contents, None).unwrap(); let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from config overrode keybinding from default config"); } @@ -767,16 +424,18 @@ mod config_test { "#; let default_config = Config::from_kdl(default_config_contents, None).unwrap(); let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); - let r_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('r')) - ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Keybinding from default config bound"); assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from custom config bound as well"); } @@ -805,24 +464,27 @@ mod config_test { "#; let default_config = Config::from_kdl(default_config_contents, None).unwrap(); let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); - let ctrl_r_in_normal_mode = config.keybinds - .get(&InputMode::Normal) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Ctrl('r')) - ); - let r_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('r')) - ); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let ctrl_r_in_normal_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Ctrl('r'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); assert_eq!(ctrl_g_normal_mode_action, None, "Keybinding from normal mode in default config cleared"); assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); @@ -850,19 +512,24 @@ mod config_test { "#; let default_config = Config::from_kdl(default_config_contents, None).unwrap(); let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); - let r_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('r')) - ); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Ctrl('g'), + ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode from default config not cleared"); assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); @@ -892,27 +559,36 @@ mod config_test { "#; let default_config = Config::from_kdl(default_config_contents, None).unwrap(); let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); - let ctrl_g_pane_mode_action = config.keybinds - .get(&InputMode::Pane) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); - let r_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('r')) - ); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); - let t_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('t')) - ); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Ctrl('g'), + ); + let ctrl_g_pane_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Ctrl('g'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let t_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('t'), + ); assert_eq!(ctrl_g_normal_mode_action, None, "First keybind uncleared in one mode"); assert_eq!(ctrl_g_pane_mode_action, None, "First keybind uncleared in another mode"); assert_eq!(z_in_pane_mode, None, "Second keybind cleared as well"); @@ -944,27 +620,30 @@ mod config_test { "#; let default_config = Config::from_kdl(default_config_contents, None).unwrap(); let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config.keybinds - .get(&InputMode::Normal) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); - let ctrl_g_pane_mode_action = config.keybinds - .get(&InputMode::Pane) - .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(&Key::Ctrl('g'))); - let r_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('r')) - ); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); - let t_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('t')) - ); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); + let ctrl_g_pane_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g')); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let t_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('t'), + ); assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode not cleared"); assert_eq!(ctrl_g_pane_mode_action, None, "First Keybind cleared in its mode"); assert_eq!(z_in_pane_mode, None, "Second keybind cleared in its mode as well"); @@ -983,11 +662,12 @@ mod config_test { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config.keybinds - .get(&InputMode::Pane) - .and_then(|pane_mode_keybindings| - pane_mode_keybindings.get(&Key::Char('z')) - ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound"); } @@ -1040,7 +720,8 @@ mod config_test { ..Default::default() } }); - assert_eq!(config.themes, Some(expected_themes), "Theme defined in config"); + let expected_themes = Themes::from_data(expected_themes); + assert_eq!(config.themes, expected_themes, "Theme defined in config"); } #[test] @@ -1109,7 +790,8 @@ mod config_test { ..Default::default() } }); - assert_eq!(config.themes, Some(expected_themes), "Theme defined in config"); + let expected_themes = Themes::from_data(expected_themes); + assert_eq!(config.themes, expected_themes, "Theme defined in config"); } #[test] @@ -1149,7 +831,8 @@ mod config_test { ..Default::default() } }); - assert_eq!(config.themes, Some(expected_themes), "Theme defined in config"); + let expected_themes = Themes::from_data(expected_themes); + assert_eq!(config.themes, expected_themes, "Theme defined in config"); } #[test] @@ -1209,7 +892,7 @@ mod config_test { rounded_corners: true } }; - assert_eq!(config.ui, Some(expected_ui_config), "Ui config defined in config"); + assert_eq!(config.ui, expected_ui_config, "Ui config defined in config"); } #[test] diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index bdf7528f45..ae280d8483 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -1,16 +1,20 @@ //! Mapping of inputs to sequences of actions. use std::collections::HashMap; +use std::str::FromStr; +use kdl::{KdlDocument, KdlValue, KdlNode}; use super::actions::Action; -use super::config; +use super::config:: ConfigError; use crate::input::{InputMode, Key}; +use crate::{kdl_arg_is_truthy, kdl_children_or_error, kdl_string_arguments, kdl_argument_values, kdl_children, kdl_name, keys_from_kdl, actions_from_kdl, kdl_children_nodes_or_error}; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; /// Used in the config struct -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct Keybinds(HashMap); +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)] +pub struct Keybinds(pub HashMap>>); +// pub struct Keybinds(HashMap); #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct ModeKeybinds(HashMap>); @@ -65,163 +69,85 @@ enum Unbind { All(bool), } -// impl Default for Keybinds { -// // Use once per codepath -// // TODO investigate why -// fn default() -> HashMap>> { -// Self::from_default_assets() -// } -// } - impl Keybinds { - pub fn new() -> Keybinds { - Keybinds(HashMap::::new()) + fn bind_actions_for_each_key(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ + let keys: Vec = keys_from_kdl!(key_block); + let actions: Vec = actions_from_kdl!(key_block); + for key in keys { + input_mode_keybinds.insert(key, actions.clone()); + } + Ok(()) } - - fn from_default_assets() -> HashMap>> { - config::Config::from_default_assets() - .expect("Keybinds from default assets Error!") - .keybinds + fn unbind_keys(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ + let keys: Vec = keys_from_kdl!(key_block); + for key in keys { + input_mode_keybinds.remove(&key); + } + Ok(()) } - -// /// Entrypoint from the config module -// pub fn get_default_keybinds_with_config(from_yaml: Option) -> Keybinds { -// let default_keybinds = match from_yaml.clone() { -// Some(keybinds) => match keybinds.unbind { -// Unbind::All(true) => Keybinds::new(), -// Unbind::All(false) | Unbind::Keys(_) => Keybinds::unbind(keybinds), -// }, -// None => Keybinds::default(), -// }; -// -// if let Some(keybinds) = from_yaml { -// default_keybinds.merge_keybinds(Keybinds::from(keybinds)) -// } else { -// default_keybinds -// } -// } - - /// Unbinds the default keybindings in relation to their mode -// fn unbind(from_yaml: KeybindsFromYaml) -> Keybinds { -// let mut keybind_config = Self::new(); -// let mut unbind_config: HashMap = HashMap::new(); -// let keybinds_from_yaml = from_yaml.keybinds; -// -// for mode in InputMode::iter() { -// if let Some(keybinds) = keybinds_from_yaml.get(&mode) { -// for keybind in keybinds { -// match keybind { -// KeyActionUnbind::Unbind(unbind) => { -// unbind_config.insert(mode, unbind.unbind.clone()); -// }, -// KeyActionUnbind::KeyAction(key_action_from_yaml) => { -// keybind_config -// .0 -// .insert(mode, ModeKeybinds::from(key_action_from_yaml.clone())); -// }, -// } -// } -// } -// } -// -// let mut default = Self::default().unbind_mode(unbind_config); -// -// // Toplevel Unbinds -// if let Unbind::Keys(_) = from_yaml.unbind { -// let mut unbind_config: HashMap = HashMap::new(); -// for mode in InputMode::iter() { -// unbind_config.insert(mode, from_yaml.unbind.clone()); -// } -// default = default.unbind_mode(unbind_config); -// }; -// -// default.merge_keybinds(keybind_config) -// } - - /// Unbind [`Key`] bindings respective to their mode - fn unbind_mode(&self, unbind: HashMap) -> Keybinds { - let mut keybinds = Keybinds::new(); - - for mode in InputMode::iter() { - if let Some(unbind) = unbind.get(&mode) { - match unbind { - Unbind::All(true) => {}, - Unbind::Keys(keys) => { - if let Some(defaults) = self.0.get(&mode) { - keybinds - .0 - .insert(mode, defaults.clone().unbind_keys(keys.to_vec())); - } - }, - Unbind::All(false) => { - if let Some(defaults) = self.0.get(&mode) { - keybinds.0.insert(mode, defaults.clone()); - } - }, - } - } else if let Some(defaults) = self.0.get(&mode) { - keybinds.0.insert(mode, defaults.clone()); + fn unbind_keys_in_all_modes(global_unbind: &KdlNode, keybinds_from_config: &mut Keybinds) -> Result<(), ConfigError> { + let keys: Vec = keys_from_kdl!(global_unbind); + for mode in keybinds_from_config.0.values_mut() { + for key in &keys { + mode.remove(&key); } } - keybinds + Ok(()) } - - /// Merges two Keybinds structs into one Keybinds struct - /// `other` overrides the ModeKeybinds of `self`. - pub fn merge_keybinds(&self, other: Keybinds) -> Keybinds { - let mut keybinds = Keybinds::new(); - - for mode in InputMode::iter() { - let mut mode_keybinds = ModeKeybinds::new(); - if let Some(keybind) = self.0.get(&mode) { - mode_keybinds.0.extend(keybind.0.clone()); - }; - if let Some(keybind) = other.0.get(&mode) { - mode_keybinds.0.extend(keybind.0.clone()); - } - if !mode_keybinds.0.is_empty() { - keybinds.0.insert(mode, mode_keybinds); - } + fn input_mode_keybindings <'a>(mode: &KdlNode, keybinds_from_config: &'a mut Keybinds) -> Result<&'a mut HashMap>, ConfigError> { + let mode_name = kdl_name!(mode); + let input_mode = InputMode::from_str(mode_name)?; + let input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&input_mode); + let clear_defaults_for_mode = kdl_arg_is_truthy!(mode, "clear-defaults"); + if clear_defaults_for_mode { + input_mode_keybinds.clear(); } - keybinds + Ok(input_mode_keybinds) } - - /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current - /// [`InputMode`] and [`Keybinds`]. - pub fn key_to_actions( - // TODO CONTINUE HERE (06/08): remove everything except this function and see if it still compiles, - // if so, convert this into the new way and add a fn from_kdl to it that will be the - // parse_keybindings function that's in the config object now - key: &Key, - raw_bytes: Vec, - mode: &InputMode, - keybinds: &HashMap>>, // TODO: make this a type - ) -> Vec { - let mode_keybind_or_action = |action: Action| { - if let Some(mode) = keybinds.get(mode) { - if let Some(actions) = mode.get(key).cloned() { - return actions; - } + pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result { + let clear_defaults = kdl_arg_is_truthy!(kdl_keybinds, "clear-defaults"); + let mut keybinds_from_config = if clear_defaults { Keybinds::default() } else { base_keybinds }; + for mode in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { + if kdl_name!(mode) == "unbind" { + continue; + } + let mut input_mode_keybinds = Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?; + let bind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); + let unbind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); + for key_block in bind_nodes { + Keybinds::bind_actions_for_each_key(key_block, &mut input_mode_keybinds)?; } - return vec![action]; -// keybinds -// .get(mode) -// .unwrap_or({ -// // create a dummy mode to recover from -// &ModeKeybinds::new() -// }) -// .0 -// .get(key) -// .cloned() -// .unwrap_or_else(|| vec![action]) + // we loop twice so that the unbinds always happen after the binds + for key_block in unbind_nodes { + Keybinds::unbind_keys(key_block, &mut input_mode_keybinds)?; + } + } + if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { + Keybinds::unbind_keys_in_all_modes(global_unbind, &mut keybinds_from_config)?; }; + Ok(keybinds_from_config) + } + pub fn get_actions_for_key_in_mode(&self, mode: &InputMode, key: &Key) -> Option<&Vec> { + self.0.get(mode) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key)) + } + pub fn get_actions_for_key_in_mode_or_default_action(&self, mode: &InputMode, key: &Key, raw_bytes: Vec) -> Vec { + self.0.get(mode) + .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key)) + .cloned() + .unwrap_or_else(|| { + vec![self.default_action_for_mode(mode, raw_bytes)] + }) + } + pub fn get_input_mode_mut(&mut self, input_mode: &InputMode) -> &mut HashMap> { + self.0.entry(*input_mode).or_insert_with(HashMap::new) + } + pub fn default_action_for_mode(&self, mode: &InputMode, raw_bytes: Vec) -> Action { match *mode { - InputMode::Normal | InputMode::Locked => { - mode_keybind_or_action(Action::Write(raw_bytes)) - }, - InputMode::RenameTab => mode_keybind_or_action(Action::TabNameInput(raw_bytes)), - InputMode::RenamePane => mode_keybind_or_action(Action::PaneNameInput(raw_bytes)), - _ => mode_keybind_or_action(Action::NoOp), + InputMode::Normal | InputMode::Locked => Action::Write(raw_bytes), + InputMode::RenameTab => Action::TabNameInput(raw_bytes), + InputMode::RenamePane => Action::PaneNameInput(raw_bytes), + _ => Action::NoOp, } } } @@ -248,22 +174,22 @@ impl ModeKeybinds { } } -impl From for Keybinds { - fn from(keybinds_from_yaml: KeybindsFromYaml) -> Keybinds { - let mut keybinds = Keybinds::new(); - - for mode in InputMode::iter() { - let mut mode_keybinds = ModeKeybinds::new(); - if let Some(key_action) = keybinds_from_yaml.keybinds.get(&mode) { - for keybind in key_action { - mode_keybinds = mode_keybinds.merge(ModeKeybinds::from(keybind.clone())); - } - } - keybinds.0.insert(mode, mode_keybinds); - } - keybinds - } -} +// impl From for Keybinds { +// fn from(keybinds_from_yaml: KeybindsFromYaml) -> Keybinds { +// let mut keybinds = Keybinds::new(); +// +// for mode in InputMode::iter() { +// let mut mode_keybinds = ModeKeybinds::new(); +// if let Some(key_action) = keybinds_from_yaml.keybinds.get(&mode) { +// for keybind in key_action { +// mode_keybinds = mode_keybinds.merge(ModeKeybinds::from(keybind.clone())); +// } +// } +// keybinds.0.insert(mode, mode_keybinds); +// } +// keybinds +// } +// } /// For each [`Key`] assigned to [`Action`]s, /// map the [`Action`]s to the [`Key`] diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index c5ff69592d..0a3b5d5ff8 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -5,6 +5,7 @@ use clap::{ArgEnum, Args}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::str::FromStr; +use kdl::{KdlDocument, KdlValue, KdlNode}; #[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ArgEnum)] pub enum OnForceClose { @@ -137,6 +138,72 @@ impl Options { Options::default() } } +// pub fn from_kdl(kdl_options: &KdlDocument) -> Self { +// let on_force_close = kdl_options.get("on_force_close") +// .and_then(|on_force_close| on_force_close.entries().iter().next()) +// .and_then(|on_force_close| on_force_close.value().as_string()) +// .and_then(|on_force_close| ::from_str(on_force_close).ok()); +// let simplified_ui = kdl_options.get("simplified_ui") +// .and_then(|simplified_ui| simplified_ui.entries().iter().next()) +// .and_then(|simplified_ui| simplified_ui.value().as_bool()); +// let default_shell = kdl_options.get("default_shell") +// .and_then(|default_shell| default_shell.entries().iter().next()) +// .and_then(|default_shell| default_shell.value().as_string()) +// .map(|default_shell| PathBuf::from(default_shell)); +// let pane_frames = kdl_options.get("pane_frames") +// .and_then(|pane_frames| pane_frames.entries().iter().next()) +// .and_then(|pane_frames| pane_frames.value().as_bool()); +// let theme = kdl_options.get("theme") +// .and_then(|theme| theme.entries().iter().next()) +// .and_then(|theme| theme.value().as_string()) +// .map(|theme| theme.to_string()); +// let default_mode = kdl_options.get("default_mode") +// .and_then(|default_mode| default_mode.entries().iter().next()) +// .and_then(|default_mode| default_mode.value().as_string()) +// .and_then(|default_mode| InputMode::try_from(default_mode).ok()); +// let mouse_mode = kdl_options.get("mouse_mode") +// .and_then(|mouse_mode| mouse_mode.entries().iter().next()) +// .and_then(|mouse_mode| mouse_mode.value().as_bool()); +// let scroll_buffer_size = kdl_options.get("scroll_buffer_size") +// .and_then(|scroll_buffer_size| scroll_buffer_size.entries().iter().next()) +// .and_then(|scroll_buffer_size| scroll_buffer_size.value().as_i64()) +// .map(|scroll_buffer_size| scroll_buffer_size as usize); +// let copy_command = kdl_options.get("copy_command") +// .and_then(|copy_command| copy_command.entries().iter().next()) +// .and_then(|copy_command| copy_command.value().as_string()) +// .map(|copy_command| copy_command.to_string()); +// let copy_clipboard = kdl_options.get("copy_clipboard") +// .and_then(|copy_clipboard| copy_clipboard.entries().iter().next()) +// .and_then(|copy_clipboard| copy_clipboard.value().as_string()) +// .and_then(|on_force_close| ::from_str(on_force_close).ok()); +// let copy_on_select = kdl_options.get("copy_on_select") +// .and_then(|copy_on_select| copy_on_select.entries().iter().next()) +// .and_then(|copy_on_select| copy_on_select.value().as_bool()); +// let scrollback_editor = kdl_options.get("scrollback_editor") +// .and_then(|scrollback_editor| scrollback_editor.entries().iter().next()) +// .and_then(|scrollback_editor| scrollback_editor.value().as_string()) +// .map(|scrollback_editor| PathBuf::from(scrollback_editor)); +// let mirror_session = kdl_options.get("mirror_session") +// .and_then(|mirror_session| mirror_session.entries().iter().next()) +// .and_then(|mirror_session| mirror_session.value().as_bool()); +// Options { +// simplified_ui, +// theme, +// default_mode, +// default_shell, +// default_layout: Some(PathBuf::from("default")), // TODO +// layout_dir: None, // TODO +// mouse_mode, +// pane_frames, +// mirror_session, +// on_force_close, +// scroll_buffer_size, +// copy_command, +// copy_clipboard, +// copy_on_select, +// scrollback_editor, +// } +// } /// Merges two [`Options`] structs, a `Some` in `other` /// will supersede a `Some` in `self` diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index bb732c618d..4abbb17fce 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -6,11 +6,14 @@ use std::fs; use std::path::{Path, PathBuf}; use thiserror::Error; +use kdl::KdlNode; +use crate::{kdl_children_nodes_or_error, kdl_name, kdl_property_first_arg_as_string, kdl_children_property_first_arg_as_string, kdl_children_property_first_arg_as_bool}; + use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use url::Url; -use super::config::ConfigFromYaml; +use super::config::{ConfigError, ConfigFromYaml}; use super::layout::{RunPlugin, RunPluginLocation}; pub use crate::data::PluginTag; use crate::setup; @@ -37,6 +40,26 @@ impl PluginsConfig { pub fn from_data(data: HashMap) -> Self { PluginsConfig(data) } + pub fn from_kdl(kdl_plugin_config: &KdlNode) -> Result { + let mut plugins: HashMap = HashMap::new(); + for plugin_config in kdl_children_nodes_or_error!(kdl_plugin_config, "no plugin config found") { + let plugin_name = kdl_name!(plugin_config); + let plugin_tag = PluginTag::new(plugin_name); + let path = kdl_children_property_first_arg_as_string!(plugin_config, "path") + .map(|path| PathBuf::from(path)) + .ok_or::>("Plugin path not found".into())?; + let allow_exec_host_cmd = kdl_children_property_first_arg_as_bool!(plugin_config, "_allow_exec_host_cmd") + .unwrap_or(false); + let plugin_config = PluginConfig { + path, + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(plugin_tag.clone()), + _allow_exec_host_cmd: allow_exec_host_cmd, + }; + plugins.insert(plugin_tag, plugin_config); + } + Ok(PluginsConfig(plugins)) + } /// Entrypoint from the config module // pub fn get_plugins_with_default(user_plugins: Self) -> Self { diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index 218977344f..e4de311fd2 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -4,7 +4,11 @@ use serde::{ }; use std::{collections::HashMap, fmt}; +use kdl::KdlNode; +use crate::{entry_count, kdl_entries_as_i64, kdl_first_entry_as_string, kdl_first_entry_as_i64, kdl_children_or_error, kdl_name, kdl_children_nodes_or_error, kdl_get_child, kdl_children_property_first_arg_as_bool}; + use super::options::Options; +use super::config::ConfigError; use crate::data::{Palette, PaletteColor}; use crate::shared::detect_theme_hue; @@ -17,15 +21,64 @@ pub struct UiConfig { pub pane_frames: FrameConfig, } +impl UiConfig { + pub fn from_kdl(kdl_ui_config: &KdlNode) -> Result { + let mut ui_config = UiConfig::default(); + if let Some(pane_frames) = kdl_get_child!(kdl_ui_config, "pane_frames") { + let rounded_corners = kdl_children_property_first_arg_as_bool!(pane_frames, "rounded_corners").unwrap_or(false); + let frame_config = FrameConfig { rounded_corners }; + ui_config.pane_frames = frame_config; + } + Ok(ui_config) + } +} + #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] pub struct FrameConfig { pub rounded_corners: bool, } +#[derive(Debug, Clone, PartialEq, Default)] +pub struct Themes(HashMap); + +impl Themes { + pub fn from_data(theme_data: HashMap) -> Self { + Themes(theme_data) + } + pub fn from_kdl(themes_from_kdl: &KdlNode) -> Result { + let mut themes: HashMap = HashMap::new(); + for theme_config in kdl_children_nodes_or_error!(themes_from_kdl, "no themes found") { + let theme_name = kdl_name!(theme_config); + let theme_colors = kdl_children_or_error!(theme_config, "empty theme"); + let theme = Theme { + palette: Palette { + fg: PaletteColor::try_from(("fg", theme_colors))?, + bg: PaletteColor::try_from(("bg", theme_colors))?, + red: PaletteColor::try_from(("red", theme_colors))?, + green: PaletteColor::try_from(("green", theme_colors))?, + yellow: PaletteColor::try_from(("yellow", theme_colors))?, + blue: PaletteColor::try_from(("blue", theme_colors))?, + magenta: PaletteColor::try_from(("magenta", theme_colors))?, + orange: PaletteColor::try_from(("orange", theme_colors))?, + cyan: PaletteColor::try_from(("cyan", theme_colors))?, + black: PaletteColor::try_from(("black", theme_colors))?, + white: PaletteColor::try_from(("white", theme_colors))?, + ..Default::default() + } + }; + themes.insert(theme_name.into(), theme); + } + let themes = Themes::from_data(themes); + Ok(themes) + } + pub fn get_theme(&self, theme_name: &str) -> Option<&Theme> { + self.0.get(theme_name) + } +} + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct Theme { #[serde(flatten)] - // palette: PaletteFromYaml, pub palette: Palette, } diff --git a/zellij-utils/src/kdl.rs b/zellij-utils/src/kdl.rs index c56d56df03..5b0d575c9f 100644 --- a/zellij-utils/src/kdl.rs +++ b/zellij-utils/src/kdl.rs @@ -2,10 +2,10 @@ // use super::layout::TabLayout; use crate::data::{InputMode, Key, CharOrArrow, PaletteColor}; -use crate::input::options::OnForceClose; +use crate::input::options::{Options, OnForceClose, Clipboard}; use serde::{Deserialize, Serialize}; -use kdl::{KdlDocument, KdlValue}; +use kdl::{KdlDocument, KdlValue, KdlNode}; use std::str::FromStr; use std::path::PathBuf; @@ -19,7 +19,7 @@ macro_rules! parse_kdl_action_arguments { ( $action_name:expr, $action_arguments:expr ) => { { if !$action_arguments.is_empty() { - Err(format!("Failed to parse action: {}", $action_name)) + Err(format!("Failed to parse action: {}", $action_name).into()) } else { match $action_name { "Quit" => Ok(Action::Quit), @@ -51,7 +51,7 @@ macro_rules! parse_kdl_action_arguments { "Copy" => Ok(Action::Copy), "Confirm" => Ok(Action::Confirm), "Deny" => Ok(Action::Deny), - _ => Err(format!("Error parsing enum variant: {:?}", $action_name)) + _ => Err(format!("Error parsing enum variant: {:?}", $action_name).into()) } } } @@ -66,7 +66,7 @@ macro_rules! parse_kdl_action_u8_arguments { match kdl_value.as_i64() { Some(int_value) => bytes.push(int_value as u8), None => { - return Err(format!("Failed to parse action: {}", $action_name)); + return Err(format!("Failed to parse action: {}", $action_name).into()); } } }; @@ -109,7 +109,7 @@ macro_rules! parse_kdl_action_char_or_string_arguments { match kdl_value.as_string() { Some(string_value) => chars_to_write.push_str(string_value), None => { - return Err(format!("Failed to parse action: {}", $action_name)); + return Err(format!("Failed to parse action: {}", $action_name).into()); } } }; @@ -117,7 +117,78 @@ macro_rules! parse_kdl_action_char_or_string_arguments { }} } -pub fn kdl_string_arguments <'a>(arguments: impl Iterator) -> Result, String> { +#[macro_export] +macro_rules! kdl_arg_is_truthy { + ( $kdl_node:expr, $arg_name:expr ) => { + $kdl_node.get("clear-defaults").and_then(|c| c.value().as_bool()).unwrap_or(false) + } +} + +#[macro_export] +macro_rules! kdl_children_nodes_or_error { + ( $kdl_node:expr, $error:expr ) => { + $kdl_node.children().ok_or(ConfigError::KdlParsingError($error.into()))?.nodes() + } +} + +#[macro_export] +macro_rules! kdl_children_or_error { + ( $kdl_node:expr, $error:expr ) => { + $kdl_node.children().ok_or(ConfigError::KdlParsingError($error.into()))? + } +} + +#[macro_export] +macro_rules! kdl_children { + ( $kdl_node:expr ) => { + $kdl_node.children().iter().copied().collect() + } +} + +#[macro_export] +macro_rules! kdl_string_arguments { + ( $kdl_node:expr ) => {{ + let res: Result, _> = $kdl_node.entries().iter().map(|e| e.value().as_string().ok_or(ConfigError::KdlParsingError("Not a string".into()))).collect(); + res? + }} +} + +#[macro_export] +macro_rules! kdl_argument_values { + ( $kdl_node:expr ) => { + $kdl_node.entries().iter().map(|arg| arg.value()).collect() + } +} + +#[macro_export] +macro_rules! kdl_name { + ( $kdl_node:expr ) => { + $kdl_node.name().value() + } +} + +#[macro_export] +macro_rules! keys_from_kdl { + ( $kdl_node:expr ) => { + kdl_string_arguments!($kdl_node) + .iter() + .map(|k| Key::from_str(k)) + .collect::>()? + } +} + +#[macro_export] +macro_rules! actions_from_kdl { + ( $kdl_node:expr ) => { + kdl_children_nodes_or_error!($kdl_node, "no actions found for key_block") + .iter() + .map(|kdl_action| Action::try_from(kdl_action)) + .collect::>()? + } +} + + +pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, String> { let mut args: Vec = vec![]; for kdl_value in arguments { match kdl_value.as_string() { @@ -138,7 +209,7 @@ pub fn kdl_child_string_value_for_entry <'a>(command_metadata: &'a KdlDocument, } impl Action { - pub fn new_from_bytes(action_name: &str, bytes: Vec) -> Result { + pub fn new_from_bytes(action_name: &str, bytes: Vec) -> Result> { match action_name { "Write" => { Ok(Action::Write(bytes)) @@ -156,16 +227,16 @@ impl Action { as u32; Ok(Action::GoToTab(tab_index)) } - _ => Err(format!("Cannot create action: {} from bytes: {:?}", action_name, bytes)), + _ => Err(format!("Cannot create action: {} from bytes: {:?}", action_name, bytes).into()), } } - pub fn new_from_string(action_name: &str, string: String) -> Result { + pub fn new_from_string(action_name: &str, string: String) -> Result> { match action_name { "WriteChars" => Ok(Action::WriteChars(string)), "SwitchToMode" => { match InputMode::try_from(string.as_str()) { Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)), - Err(_e) => return Err(format!("Failed to parse SwitchToMode. Unknown InputMode: {}", string)), + Err(_e) => return Err(format!("Failed to parse SwitchToMode. Unknown InputMode: {}", string).into()), } }, "Resize" => { @@ -199,14 +270,15 @@ impl Action { Ok(Action::NewPane(Some(direction))) } } - _ => Err(format!("Cannot create action: '{}' from string: '{:?}'", action_name, string)), + _ => Err(format!("Cannot create action: '{}' from string: '{:?}'", action_name, string).into()), } } } impl TryFrom<(&str, &KdlDocument)> for PaletteColor { - type Error = String; - fn try_from((color_name, theme_colors): (&str, &KdlDocument)) -> Result { + type Error = Box; + + fn try_from((color_name, theme_colors): (&str, &KdlDocument)) -> Result { let color = theme_colors.get(color_name).ok_or(format!("Failed to parse color"))?; let entry_count = entry_count!(color); let is_rgb = || entry_count == 3; @@ -255,14 +327,20 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor { let n = kdl_first_entry_as_i64!(color).ok_or(format!("Failed to parse color"))?; Ok(PaletteColor::EightBit(n as u8)) // TODO: test values greater than u8 bounds } else { - Err(format!("Failed to parse color")) + Err("Failed to parse color".into()) } } } -impl TryFrom<(&str, Vec<&KdlValue>, Vec<&KdlDocument>)> for Action { - type Error = String; - fn try_from((action_name, action_arguments, action_children): (&str, Vec<&KdlValue>, Vec<&KdlDocument>)) -> Result { +// impl TryFrom<(&str, Vec<&KdlValue>, Vec<&KdlDocument>)> for Action { +impl TryFrom<&KdlNode> for Action { + type Error = Box; + // fn try_from((action_name, action_arguments, action_children): (&str, Vec<&KdlValue>, Vec<&KdlDocument>)) -> Result { + fn try_from(kdl_action: &KdlNode) -> Result { + + let action_name = kdl_name!(kdl_action); + let action_arguments: Vec<&KdlValue> = kdl_argument_values!(kdl_action); + let action_children: Vec<&KdlDocument> = kdl_children!(kdl_action); match action_name { "Quit" => parse_kdl_action_arguments!(action_name, action_arguments), "FocusNextPane" => parse_kdl_action_arguments!(action_name, action_arguments), @@ -308,9 +386,9 @@ impl TryFrom<(&str, Vec<&KdlValue>, Vec<&KdlDocument>)> for Action { "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "Run" => { let arguments = action_arguments.iter().copied(); - let mut args = kdl_string_arguments(arguments)?; + let mut args = kdl_arguments_that_are_strings(arguments)?; if args.is_empty() { - return Err(format!("No command found in Run action")); + return Err("No command found in Run action".into()); } let command = args.remove(0); let command_metadata = action_children.iter().next(); @@ -329,7 +407,7 @@ impl TryFrom<(&str, Vec<&KdlValue>, Vec<&KdlDocument>)> for Action { Ok(Action::Run(run_command_action)) } _ => { - Err(format!("Failed to parse action: {}", action_name)) + Err(format!("Failed to parse action: {}", action_name).into()) } } } @@ -429,3 +507,101 @@ impl TryFrom<&KdlValue> for Key { } } } + +#[macro_export] +macro_rules! kdl_property_first_arg_as_string { + ( $kdl_node:expr, $property_name:expr ) => { + $kdl_node.get($property_name) + .and_then(|p| p.entries().iter().next()) + .and_then(|p| p.value().as_string()) + } +} + +#[macro_export] +macro_rules! kdl_children_property_first_arg_as_string { + ( $kdl_node:expr, $property_name:expr ) => { + $kdl_node.children() + .and_then(|c| c.get($property_name)) + .and_then(|p| p.entries().iter().next()) + .and_then(|p| p.value().as_string()) + } +} + +#[macro_export] +macro_rules! kdl_property_first_arg_as_bool { + ( $kdl_node:expr, $property_name:expr ) => { + $kdl_node.get($property_name) + .and_then(|p| p.entries().iter().next()) + .and_then(|p| p.value().as_bool()) + } +} + +#[macro_export] +macro_rules! kdl_children_property_first_arg_as_bool { + ( $kdl_node:expr, $property_name:expr ) => { + $kdl_node.children() + .and_then(|c| c.get($property_name)) + .and_then(|p| p.entries().iter().next()) + .and_then(|p| p.value().as_bool()) + } +} + +#[macro_export] +macro_rules! kdl_property_first_arg_as_i64 { + ( $kdl_node:expr, $property_name:expr ) => { + $kdl_node.get($property_name) + .and_then(|p| p.entries().iter().next()) + .and_then(|p| p.value().as_i64()) + } +} + +#[macro_export] +macro_rules! kdl_get_child { + ( $kdl_node:expr, $child_name:expr ) => { + $kdl_node.children() + .and_then(|c| c.get($child_name)) + } +} + +impl Options { + pub fn from_kdl(kdl_options: &KdlDocument) -> Self { + let on_force_close = kdl_property_first_arg_as_string!(kdl_options, "on_force_close") + .and_then(|arg| OnForceClose::from_str(arg).ok()); + let simplified_ui = kdl_property_first_arg_as_bool!(kdl_options, "simplified_ui"); + let default_shell = kdl_property_first_arg_as_string!(kdl_options, "default_shell") + .map(|default_shell| PathBuf::from(default_shell)); + let pane_frames = kdl_property_first_arg_as_bool!(kdl_options, "pane_frames"); + let theme = kdl_property_first_arg_as_string!(kdl_options, "theme") + .map(|theme| theme.to_string()); + let default_mode = kdl_property_first_arg_as_string!(kdl_options, "default_mode") + .and_then(|default_mode| InputMode::try_from(default_mode).ok()); + let mouse_mode = kdl_property_first_arg_as_bool!(kdl_options, "mouse_mode"); + let scroll_buffer_size = kdl_property_first_arg_as_i64!(kdl_options, "scroll_buffer_size") + .map(|scroll_buffer_size| scroll_buffer_size as usize); + let copy_command = kdl_property_first_arg_as_string!(kdl_options, "copy_command") + .map(|copy_command| copy_command.to_string()); + let copy_clipboard = kdl_property_first_arg_as_string!(kdl_options, "copy_clipboard") + .and_then(|on_force_close| Clipboard::from_str(on_force_close).ok()); + let copy_on_select = kdl_property_first_arg_as_bool!(kdl_options, "copy_on_select"); + let scrollback_editor = kdl_property_first_arg_as_string!(kdl_options, "scrollback_editor") + .map(|scrollback_editor| PathBuf::from(scrollback_editor)); + let mirror_session = kdl_property_first_arg_as_bool!(kdl_options, "mirror_session"); + Options { + simplified_ui, + theme, + default_mode, + default_shell, + default_layout: Some(PathBuf::from("default")), // TODO + layout_dir: None, // TODO + mouse_mode, + pane_frames, + mirror_session, + on_force_close, + scroll_buffer_size, + copy_command, + copy_clipboard, + copy_on_select, + scrollback_editor, + } + } +} From 256005d8729ad6964f2e9d76843eecdad57d732b Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 11 Aug 2022 09:14:35 +0200 Subject: [PATCH 05/55] work --- src/commands.rs | 81 +-- zellij-client/src/input_handler.rs | 4 +- zellij-client/src/lib.rs | 5 +- zellij-server/src/lib.rs | 45 +- zellij-server/src/pty.rs | 25 +- zellij-server/src/route.rs | 4 +- zellij-utils/assets/layouts/compact.kdl | 13 + zellij-utils/assets/layouts/default.kdl | 18 + .../assets/layouts/disable-status-bar.kdl | 13 + zellij-utils/assets/layouts/no-plugins.kdl | 8 + zellij-utils/assets/layouts/strider.kdl | 30 + zellij-utils/src/input/actions.rs | 4 +- zellij-utils/src/input/command.rs | 22 + zellij-utils/src/input/config.rs | 2 + zellij-utils/src/input/layout.rs | 677 +++++++++++++++--- zellij-utils/src/ipc.rs | 5 +- zellij-utils/src/kdl.rs | 60 +- zellij-utils/src/setup.rs | 78 +- 18 files changed, 870 insertions(+), 224 deletions(-) create mode 100644 zellij-utils/assets/layouts/compact.kdl create mode 100644 zellij-utils/assets/layouts/default.kdl create mode 100644 zellij-utils/assets/layouts/disable-status-bar.kdl create mode 100644 zellij-utils/assets/layouts/no-plugins.kdl create mode 100644 zellij-utils/assets/layouts/strider.kdl diff --git a/src/commands.rs b/src/commands.rs index 5abedf3d15..46e914164b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -285,7 +285,7 @@ pub(crate) fn start_client(opts: CliArgs) { let attach_layout = match client { ClientInfo::Attach(_, _) => None, - ClientInfo::New(_) => layout, + ClientInfo::New(_) => Some(layout), }; if create { @@ -314,46 +314,47 @@ pub(crate) fn start_client(opts: CliArgs) { config, config_options, ClientInfo::New(session_name), - layout, + Some(layout), ); } else { - if let Some(layout_some) = layout.clone() { - if let Some(session_name) = layout_some.session.name { - if layout_some.session.attach.unwrap() { - let client = attach_with_session_name( - Some(session_name), - config_options.clone(), - true, - ); - - let attach_layout = match client { - ClientInfo::Attach(_, _) => None, - ClientInfo::New(_) => layout, - }; - - start_client_impl( - Box::new(os_input), - opts, - config, - config_options, - client, - attach_layout, - ); - } else { - start_client_plan(session_name.clone()); - start_client_impl( - Box::new(os_input), - opts, - config, - config_options, - ClientInfo::New(session_name), - layout, - ); - } - - process::exit(0); - } - } + // TODO: bring this back +// if let Some(layout_some) = layout.clone() { +// if let Some(session_name) = layout_some.session.name { +// if layout_some.session.attach.unwrap() { +// let client = attach_with_session_name( +// Some(session_name), +// config_options.clone(), +// true, +// ); +// +// let attach_layout = match client { +// ClientInfo::Attach(_, _) => None, +// ClientInfo::New(_) => layout, +// }; +// +// start_client_impl( +// Box::new(os_input), +// opts, +// config, +// config_options, +// client, +// attach_layout, +// ); +// } else { +// start_client_plan(session_name.clone()); +// start_client_impl( +// Box::new(os_input), +// opts, +// config, +// config_options, +// ClientInfo::New(session_name), +// layout, +// ); +// } +// +// process::exit(0); +// } +// } let session_name = names::Generator::default().next().unwrap(); start_client_plan(session_name.clone()); @@ -363,7 +364,7 @@ pub(crate) fn start_client(opts: CliArgs) { config, config_options, ClientInfo::New(session_name), - layout, + Some(layout), ); } } diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs index bab0cd33e5..949a5c702a 100644 --- a/zellij-client/src/input_handler.rs +++ b/zellij-client/src/input_handler.rs @@ -206,7 +206,7 @@ impl InputHandler { }, // Actions, that are independent from the specific client // and not session idempotent should be specified here - Action::NewTab(_) + Action::NewTab(..) | Action::Run(_) | Action::NewPane(_) | Action::WriteChars(_) @@ -287,7 +287,7 @@ impl InputHandler { | Action::Run(_) | Action::ToggleFloatingPanes | Action::TogglePaneEmbedOrFloating - | Action::NewTab(_) + | Action::NewTab(..) | Action::GoToNextTab | Action::GoToPreviousTab | Action::CloseTab diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index a83e163e68..ea9b388064 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -31,7 +31,7 @@ use zellij_utils::{ ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg}, termwiz::input::InputEvent, }; -use zellij_utils::{cli::CliArgs, input::layout::LayoutFromYaml}; +use zellij_utils::{cli::CliArgs, input::layout::{Layout, LayoutFromYaml}}; /// Instructions related to the client-side application #[derive(Debug, Clone)] @@ -125,7 +125,8 @@ pub fn start_client( config: Config, config_options: Options, info: ClientInfo, - layout: Option, + // layout: Option, + layout: Option, ) { info!("Starting Zellij client!"); let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l\u{1b}[?12l"; diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 7b9bf798bc..9ecafdb80c 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -44,7 +44,7 @@ use zellij_utils::{ input::{ command::{RunCommand, TerminalAction}, get_mode_info, - layout::LayoutFromYaml, + layout::{Layout, LayoutFromYaml}, options::Options, plugins::PluginsConfig, }, @@ -61,7 +61,8 @@ pub enum ServerInstruction { ClientAttributes, Box, Box, - Box, + // Box, + Box, ClientId, Option, ), @@ -306,7 +307,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { }) }); - let spawn_tabs = |tab_layout| { + let spawn_tabs = |tab_layout, tab_name| { session_data .read() .unwrap() @@ -316,33 +317,46 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { .send_to_pty(PtyInstruction::NewTab( default_shell.clone(), tab_layout, + tab_name, client_id, )) .unwrap() }; - if !&layout.tabs.is_empty() { - for tab_layout in layout.clone().tabs { - spawn_tabs(Some(tab_layout.clone())); + // if !&layout.tabs.is_empty() { + if !&layout.has_tabs() { + // for tab_layout in layout.clone().tabs { + for (tab_layout, tab_name) in layout.tabs() { + spawn_tabs(Some(tab_layout.clone()), Some(tab_name)); } - let focused_tab = layout - .tabs - .into_iter() - .enumerate() - .find(|(_, tab_layout)| tab_layout.focus.unwrap_or(false)); - if let Some((tab_index, _)) = focused_tab { + if let Some(focused_tab_index) = layout.focused_tab_index() { session_data .read() .unwrap() .as_ref() .unwrap() .senders - .send_to_pty(PtyInstruction::GoToTab((tab_index + 1) as u32, client_id)) + .send_to_pty(PtyInstruction::GoToTab((focused_tab_index + 1) as u32, client_id)) .unwrap(); } +// let focused_tab = layout +// .tabs +// .into_iter() +// .enumerate() +// .find(|(_, tab_layout)| tab_layout.focus.unwrap_or(false)); +// if let Some((tab_index, _)) = focused_tab { +// session_data +// .read() +// .unwrap() +// .as_ref() +// .unwrap() +// .senders +// .send_to_pty(PtyInstruction::GoToTab((tab_index + 1) as u32, client_id)) +// .unwrap(); +// } } else { - spawn_tabs(None); + spawn_tabs(None, None); } session_data .read() @@ -564,7 +578,8 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { pub struct SessionOptions { pub opts: Box, pub config_options: Box, - pub layout: Box, + // pub layout: Box, + pub layout: Box, pub plugins: Option, } diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index f1489ea639..fc7843509e 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -33,7 +33,8 @@ pub(crate) enum PtyInstruction { SpawnTerminalHorizontally(Option, ClientId), UpdateActivePane(Option, ClientId), GoToTab(TabIndex, ClientId), - NewTab(Option, Option, ClientId), + // NewTab(Option, Option, ClientId), + NewTab(Option, Option, Option, ClientId), // the String is the tab name ClosePane(PaneId), CloseTab(Vec), Exit, @@ -67,7 +68,8 @@ pub(crate) struct Pty { use std::convert::TryFrom; -pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { +// pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { +pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { loop { let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Pty((&event).into())); @@ -136,16 +138,17 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { .send_to_screen(ScreenInstruction::GoToTab(tab_index, Some(client_id))) .unwrap(); }, - PtyInstruction::NewTab(terminal_action, tab_layout, client_id) => { - let tab_name = tab_layout.as_ref().and_then(|layout| { - if layout.name.is_empty() { - None - } else { - Some(layout.name.clone()) - } - }); + PtyInstruction::NewTab(terminal_action, tab_layout, tab_name, client_id) => { +// let tab_name = tab_layout.as_ref().and_then(|layout| { +// if layout.name.is_empty() { +// None +// } else { +// Some(layout.name.clone()) +// } +// }); - let merged_layout = layout.template.clone().insert_tab_layout(tab_layout); + // let merged_layout = layout.template.clone().insert_tab_layout(tab_layout); + let merged_layout = tab_layout.map(|t| layout.insert_tab_layout(t)).unwrap_or_else(|| *layout.clone()); let layout: Layout = Layout::try_from(merged_layout).unwrap_or_else(|err| panic!("{}", err)); diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 4420cf36f2..92eac11d85 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -302,11 +302,11 @@ fn route_action( .send_to_screen(ScreenInstruction::CloseFocusedPane(client_id)) .unwrap(); }, - Action::NewTab(tab_layout) => { + Action::NewTab(tab_layout, tab_name) => { let shell = session.default_shell.clone(); session .senders - .send_to_pty(PtyInstruction::NewTab(shell, tab_layout, client_id)) + .send_to_pty(PtyInstruction::NewTab(shell, tab_layout, tab_name, client_id)) .unwrap(); }, Action::GoToNextTab => { diff --git a/zellij-utils/assets/layouts/compact.kdl b/zellij-utils/assets/layouts/compact.kdl new file mode 100644 index 0000000000..d20ff08af7 --- /dev/null +++ b/zellij-utils/assets/layouts/compact.kdl @@ -0,0 +1,13 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:compact-bar" diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl new file mode 100644 index 0000000000..fd07ade1d5 --- /dev/null +++ b/zellij-utils/assets/layouts/default.kdl @@ -0,0 +1,18 @@ +layout { + parts direction="Horizontal" { + layout size=1 { + borderless true + plugin { + location "zellij:tab-bar" + } + } + tabs; + layout size=2 { + borderless true + plugin { + location "zellij:status-bar" + } + } + } +} + diff --git a/zellij-utils/assets/layouts/disable-status-bar.kdl b/zellij-utils/assets/layouts/disable-status-bar.kdl new file mode 100644 index 0000000000..1077939803 --- /dev/null +++ b/zellij-utils/assets/layouts/disable-status-bar.kdl @@ -0,0 +1,13 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true diff --git a/zellij-utils/assets/layouts/no-plugins.kdl b/zellij-utils/assets/layouts/no-plugins.kdl new file mode 100644 index 0000000000..808d686092 --- /dev/null +++ b/zellij-utils/assets/layouts/no-plugins.kdl @@ -0,0 +1,8 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + body: true +tabs: + - direction: Vertical diff --git a/zellij-utils/assets/layouts/strider.kdl b/zellij-utils/assets/layouts/strider.kdl new file mode 100644 index 0000000000..26e1eba4f7 --- /dev/null +++ b/zellij-utils/assets/layouts/strider.kdl @@ -0,0 +1,30 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" +tabs: + - direction: Vertical + parts: + - direction: Horizontal + split_size: + Percent: 20 + run: + plugin: + location: "zellij:strider" + - direction: Horizontal diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 1f236a944a..78c90d82e3 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,7 +1,7 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::TabLayout; +use super::layout::{Layout, TabLayout}; use crate::data::InputMode; use crate::input::options::OnForceClose; use serde::{Deserialize, Serialize}; @@ -121,7 +121,7 @@ pub enum Action { PaneNameInput(Vec), UndoRenamePane, /// Create a new tab, optionally with a specified tab layout. - NewTab(Option), + NewTab(Option, Option), // the String is the tab name /// Do nothing. NoOp, /// Go to the next tab. diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs index 6f093a4a8b..8321c77fae 100644 --- a/zellij-utils/src/input/command.rs +++ b/zellij-utils/src/input/command.rs @@ -3,6 +3,10 @@ use super::actions::Direction; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use kdl::*; +use crate::{kdl_get_child, kdl_get_child_entry_string_value, kdl_string_arguments}; +use crate::input::config::ConfigError; + #[derive(Debug, Clone)] pub enum TerminalAction { OpenFile(PathBuf, Option), // path to file and optional line_number @@ -19,6 +23,24 @@ pub struct RunCommand { pub cwd: Option, } +impl RunCommand { + pub fn from_kdl(kdl_node: &KdlNode) -> Result { + let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or(ConfigError::KdlParsingError("Command must have a cmd value".into()))?); + let cwd = kdl_get_child_entry_string_value!(kdl_node, "cmd").map(|c| PathBuf::from(c)); + let args = match kdl_get_child!(kdl_node, "args") { + Some(kdl_args) => { + kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect() + }, + None => vec![] + }; + Ok(RunCommand { + command, + args, + cwd, + }) + } +} + /// Intermediate representation #[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)] pub struct RunCommandAction { diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index a163e5376b..92e6a35610 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -132,6 +132,8 @@ impl Config { pub fn from_kdl(kdl_config: &str, base_config: Option) -> ConfigResult { let mut config = base_config.unwrap_or_else(|| Config::default()); let kdl_config: KdlDocument = kdl_config.parse()?; + // TODO: handle cases where we have more than one of these blocks (eg. two "keybinds") + // this should give an informative parsing error if let Some(kdl_keybinds) = kdl_config.get("keybinds") { config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds)?; } diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index b7eff7bda2..014b04e744 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -16,7 +16,25 @@ use crate::{ pane_size::{Dimension, PaneGeom}, setup, }; -use crate::{serde, serde_yaml}; + +use kdl::*; + +use std::str::FromStr; +use std::collections::HashMap; + +use crate::{ + serde, + serde_yaml, + kdl_children, + kdl_children_nodes, + kdl_name, + kdl_document_name, + kdl_get_string_entry, + kdl_get_int_entry, + kdl_get_child_entry_bool_value, + kdl_get_child_entry_string_value, + kdl_get_child, +}; use super::{ config::ConfigFromYaml, @@ -36,20 +54,18 @@ use url::Url; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] #[serde(crate = "self::serde")] -pub enum Direction { - #[serde(alias = "horizontal")] +pub enum SplitDirection { Horizontal, - #[serde(alias = "vertical")] Vertical, } -impl Not for Direction { +impl Not for SplitDirection { type Output = Self; fn not(self) -> Self::Output { match self { - Direction::Horizontal => Direction::Vertical, - Direction::Vertical => Direction::Horizontal, + SplitDirection::Horizontal => SplitDirection::Vertical, + SplitDirection::Vertical => SplitDirection::Horizontal, } } } @@ -97,6 +113,19 @@ pub struct RunPlugin { pub location: RunPluginLocation, } +impl RunPlugin { + pub fn from_kdl(kdl_node: &KdlNode) -> Result { + let _allow_exec_host_cmd = kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false); + let string_url = kdl_get_child_entry_string_value!(kdl_node, "location").ok_or(ConfigError::KdlParsingError("Plugins must have a location".into()))?; + let url = Url::parse(string_url).map_err(|e| ConfigError::KdlParsingError(format!("Failed to aprse url: {:?}", e)))?; + let location = RunPluginLocation::try_from(url)?; + Ok(RunPlugin { + _allow_exec_host_cmd, + location, + }) + } +} + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(crate = "self::serde")] pub enum RunPluginLocation { @@ -132,14 +161,15 @@ impl fmt::Display for RunPluginLocation { } // The layout struct ultimately used to build the layouts. -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] #[serde(crate = "self::serde")] pub struct Layout { - pub direction: Direction, + pub is_body: bool, + pub direction: SplitDirection, #[serde(default)] pub pane_name: Option, #[serde(default)] - pub parts: Vec, + pub parts: LayoutParts, pub split_size: Option, pub run: Option, #[serde(default)] @@ -147,6 +177,31 @@ pub struct Layout { pub focus: Option, } +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub enum LayoutParts { + Tabs(HashMap), // String is the tab name + Panes(Vec), +} + +impl LayoutParts { + pub fn is_empty(&self) -> bool { + match self { + LayoutParts::Panes(panes) => { + panes.is_empty() + }, + LayoutParts::Tabs(tabs) => { + tabs.is_empty() + } + } + } +} + +impl Default for LayoutParts { + fn default() -> Self { + LayoutParts::Panes(vec![]) + } +} + // The struct that is used to deserialize the layout from // a yaml configuration file, is needed because of: // https://github.com/bincode-org/bincode/issues/245 @@ -438,7 +493,7 @@ fn default_as_some_true() -> Option { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(crate = "self::serde")] pub struct LayoutTemplate { - pub direction: Direction, + pub direction: SplitDirection, #[serde(default)] pub pane_name: Option, #[serde(default)] @@ -485,7 +540,7 @@ impl LayoutTemplate { #[serde(crate = "self::serde")] pub struct TabLayout { #[serde(default)] - pub direction: Direction, + pub direction: SplitDirection, pub pane_name: Option, #[serde(default)] pub borderless: bool, @@ -511,36 +566,290 @@ impl TabLayout { } impl Layout { + pub fn from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result { + match layout_path { + Some(layout_path) => { + // The way we determine where to look for the layout is similar to + // how a path would look for an executable. + // See the gh issue for more: https://github.com/zellij-org/zellij/issues/1412#issuecomment-1131559720 + if layout_path.extension().is_some() || layout_path.components().count() > 1 { + // We look localy! + Layout::from_path(layout_path) + } else { + // We look in the default dir + Layout::from_dir(layout_path, layout_dir.as_ref()) + } + }, + None => { + Layout::from_dir( + &std::path::PathBuf::from("default"), + layout_dir.as_ref(), + ) + } + } + } + pub fn from_dir( + layout: &PathBuf, + layout_dir: Option<&PathBuf>, + ) -> Result { + match layout_dir { + Some(dir) => { + let layout_path = &dir.join(layout); + if layout_path.with_extension("kdl").exists() { + Self::from_path(layout_path) + } else { + Layout::from_default_assets(layout) + } + }, + None => Layout::from_default_assets(layout), + } + } + pub fn from_path(layout_path: &Path) -> Result { + let mut layout_file = File::open(&layout_path) + .or_else(|_| File::open(&layout_path.with_extension("kdl"))) + .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; + + let mut kdl_layout = String::new(); + layout_file.read_to_string(&mut kdl_layout)?; + let kdl_layout: KdlDocument = kdl_layout.parse()?; + Layout::from_kdl(&kdl_layout, None) + } + pub fn from_kdl(kdl_layout: &KdlDocument, direction: Option) -> Result { + let mut tabs = vec![]; + let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; + fn parse_kdl_layout (kdl_layout: &KdlNode, direction: Option, tabs: &mut Vec) -> Result { + let borderless: bool = kdl_get_child_entry_bool_value!(kdl_layout, "borderless").unwrap_or(false); + let focus = kdl_get_child_entry_bool_value!(kdl_layout, "focus"); + let pane_name = kdl_get_child_entry_string_value!(kdl_layout, "name"); + let direction = direction.unwrap_or_default(); + let mut split_size = None; + if let Some(string_split_size) = kdl_get_string_entry!(kdl_layout, "size") { + // "10%" => SplitSize::Percent(10) or 10 => SplitSize::Fixed(10) + split_size = Some(SplitSize::from_str(string_split_size)?); + } + if let Some(int_split_size) = kdl_get_int_entry!(kdl_layout, "size") { + split_size = Some(SplitSize::Fixed(int_split_size as usize)); + } + let mut run = None; + if let Some(kdl_command_block) = kdl_get_child!(kdl_layout, "command") { + run = Some(Run::Command(RunCommand::from_kdl(kdl_command_block)?)); + } + if let Some(kdl_plugin_block) = kdl_get_child!(kdl_layout, "plugin") { + if run.is_some() { + return Err(ConfigError::KdlParsingError("Cannot have both a command and a plugin block for a single pane".into())); + } + run = Some(Run::Plugin(RunPlugin::from_kdl(kdl_plugin_block)?)); + } + let mut layout_parts = vec![]; + let mut is_body = false; + if let Some(kdl_parts) = kdl_get_child!(kdl_layout, "parts") { + let direction = kdl_get_string_entry!(kdl_parts, "direction").ok_or(ConfigError::KdlParsingError("no direction found for layout part".into()))?; + let direction = SplitDirection::from_str(direction)?; + // let mut parts: Vec = vec![]; + if let Some(children) = kdl_children_nodes!(kdl_layout) { + for child in children { + let child_name = kdl_name!(child); + if child_name == "layout" { + layout_parts.push(parse_kdl_layout(&child, Some(direction), tabs)?); + } else if child_name == "tabs" { + is_body = true; + if !tabs.is_empty() { + return Err(ConfigError::KdlParsingError(format!("Only one 'tabs' section allowed per layout..."))); + } + match kdl_children_nodes!(child) { + Some(children) => { + for child in children { + tabs.push(parse_kdl_layout(&child, Some(direction), tabs)?); + } + + }, + None => tabs.push(Layout::default()), + } + } else { + return Err(ConfigError::KdlParsingError(format!("Unknown layout part: {:?}", child_name))); + } + } + } + } + // let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; + Ok(Layout { + direction, + pane_name: None, // TODO + parts: LayoutParts::Panes(layout_parts), + split_size, + run, + borderless, + focus: None, // TODO + is_body, + }) + } + let mut base_layout = parse_kdl_layout(layout_node, direction, &mut tabs); + let mut parts: Vec = vec![]; + for tab in tabs { + // TODO: CONTINUE HERE (10/08) populate the LayoutParts::Tabs(tabs) thing we created + // but before that, might want to do a cargo check in zellij-utils to adjust all the + // other stuff (possible in other places too with cargo make check) + + } + base_layout + + +// // TODO: this is not right, it should be taken from parts +// let direction = kdl_get_string_entry!(layout_template_node, "direction").unwrap_or("horizontal"); +// let direction = SplitDirection::from_str(direction)?; + // TBD + // let tabs_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "tabs"); +// Layout { +// borderless: bool, +// focus: Option, +// pane_name: Option, +// direction: Direction, +// split_size: Option, +// run: Option, +// +// parts: Vec, +// } + + } +// let layout: Option = match serde_yaml::from_str(&layout) { +// Err(e) => { +// // needs direct check, as `[ErrorImpl]` is private +// // https://github.com/dtolnay/serde-yaml/issues/121 +// if layout.is_empty() { +// return Ok(Layout::default()); +// } +// return Err(ConfigError::Serde(e)); +// }, +// Ok(config) => config, +// }; +// +// match layout { +// Some(layout) => { +// for tab in layout.tabs.clone() { +// tab.check()?; +// } +// Ok(layout) +// }, +// None => Ok(LayoutFromYamlIntermediate::default()), +// } +// } + + pub fn from_default_assets(path: &Path) -> Result { + // TODO: ideally these should not be hard-coded + // we should load layouts by name from the config + // and load them from a hashmap or some such + match path.to_str() { + Some("default") => Self::default_from_assets(), + Some("strider") => Self::strider_from_assets(), + Some("disable-status-bar") => Self::disable_status_from_assets(), + Some("compact") => Self::compact_from_assets(), + None | Some(_) => Err(ConfigError::IoPath( + std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), + path.into(), + )), + } + } + + pub fn default_from_assets() -> Result { + let kdl_layout = String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?; + let kdl_layout: KdlDocument = kdl_layout.parse()?; + Layout::from_kdl(&kdl_layout, None) + } + + pub fn strider_from_assets() -> Result { + let kdl_layout = String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?; + let kdl_layout: KdlDocument = kdl_layout.parse()?; + Layout::from_kdl(&kdl_layout, None) + } + + pub fn disable_status_from_assets() -> Result { + let kdl_layout = String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?; + let kdl_layout: KdlDocument = kdl_layout.parse()?; + Layout::from_kdl(&kdl_layout, None) + } + + pub fn compact_from_assets() -> Result { + let kdl_layout = String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?; + let kdl_layout: KdlDocument = kdl_layout.parse()?; + Layout::from_kdl(&kdl_layout, None) + } + pub fn total_terminal_panes(&self) -> usize { + // TODO: better let mut total_panes = 0; - total_panes += self.parts.len(); - for part in &self.parts { - match part.run { - Some(Run::Command(_)) | None => { - total_panes += part.total_terminal_panes(); - }, - Some(Run::Plugin(_)) => {}, + match self.parts { + LayoutParts::Panes(parts) => { + total_panes += parts.len(); + for part in parts { + match part.run { + Some(Run::Command(_)) | None => { + total_panes += part.total_terminal_panes(); + }, + Some(Run::Plugin(_)) => {}, + } + } + total_panes + }, + LayoutParts::Tabs(tabs) => { + let parts = tabs.values(); + total_panes += parts.len(); + for part in parts { + match part.run { + Some(Run::Command(_)) | None => { + total_panes += part.total_terminal_panes(); + }, + Some(Run::Plugin(_)) => {}, + } + } + total_panes } } - total_panes } pub fn total_borderless_panes(&self) -> usize { + // TODO: better let mut total_borderless_panes = 0; - total_borderless_panes += self.parts.iter().filter(|p| p.borderless).count(); - for part in &self.parts { - total_borderless_panes += part.total_borderless_panes(); + match self.parts { + LayoutParts::Panes(parts) => { + total_borderless_panes += parts.iter().filter(|p| p.borderless).count(); + for part in &parts { + total_borderless_panes += part.total_borderless_panes(); + } + total_borderless_panes + }, + LayoutParts::Tabs(tabs) => { + let parts = tabs.values(); + total_borderless_panes += parts.filter(|p| p.borderless).count(); + for part in parts { + total_borderless_panes += part.total_borderless_panes(); + } + total_borderless_panes + } } - total_borderless_panes } pub fn extract_run_instructions(&self) -> Vec> { + // TODO: better let mut run_instructions = vec![]; - if self.parts.is_empty() { - run_instructions.push(self.run.clone()); - } - for part in &self.parts { - let mut current_runnables = part.extract_run_instructions(); - run_instructions.append(&mut current_runnables); + match self.parts { + LayoutParts::Panes(parts) => { + if parts.is_empty() { + run_instructions.push(self.run.clone()); + } + for part in &parts { + let mut current_runnables = part.extract_run_instructions(); + run_instructions.append(&mut current_runnables); + } + }, + LayoutParts::Tabs(tabs) => { + let parts = tabs.values(); + if parts.len() == 0 { + run_instructions.push(self.run.clone()); + } + for part in parts { + let mut current_runnables = part.extract_run_instructions(); + run_instructions.append(&mut current_runnables); + } + } } run_instructions } @@ -550,7 +859,25 @@ impl Layout { } pub fn merge_layout_parts(&mut self, mut parts: Vec) { - self.parts.append(&mut parts); + // TODO + unimplemented!() + // self.parts.append(&mut parts); + } + + pub fn insert_tab_layout(&self, tab_layout: Layout) -> Layout { + unimplemented!() + } + + pub fn has_tabs(&self) -> bool { + unimplemented!() + } + + pub fn tabs(&self) -> Vec<(Layout, String)> { // String is the tab name + unimplemented!() + } + + pub fn focused_tab_index(&self) -> Option { + unimplemented!() } fn from_vec_tab_layout(tab_layout: Vec) -> Result, ConfigError> { @@ -570,98 +897,197 @@ impl Layout { } } -fn layout_size(direction: Direction, layout: &Layout) -> usize { +fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { fn child_layout_size( - direction: Direction, - parent_direction: Direction, + direction: SplitDirection, + parent_direction: SplitDirection, layout: &Layout, ) -> usize { let size = if parent_direction == direction { 1 } else { 0 }; - if layout.parts.is_empty() { + let parts_is_empty = match layout.parts { + LayoutParts::Panes(parts) => parts.is_empty(), + LayoutParts::Tabs(tabs) => tabs.values().len() == 0 + }; + // if layout.parts.is_empty() { + if parts_is_empty { size } else { - let children_size = layout - .parts - .iter() - .map(|p| child_layout_size(direction, layout.direction, p)) - .sum(); - max(size, children_size) + match layout.parts { + LayoutParts::Panes(parts) => { + let children_size = parts + .iter() + .map(|p| child_layout_size(direction, layout.direction, p)) + .sum(); + max(size, children_size) + }, + LayoutParts::Tabs(tabs) => { + let children_size = tabs + .values() + .map(|p| child_layout_size(direction, layout.direction, p)) + .sum(); + max(size, children_size) + } + } } } child_layout_size(direction, direction, layout) } fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneGeom)> { - let mut pane_positions = Vec::new(); - let sizes: Vec> = layout.parts.iter().map(|part| part.split_size).collect(); - - let mut split_geom = Vec::new(); - let (mut current_position, split_dimension_space, mut inherited_dimension) = - match layout.direction { - Direction::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), - Direction::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), - }; - - let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); + match layout.parts { + LayoutParts::Panes(parts) => { + let mut pane_positions = Vec::new(); + let sizes: Vec> = parts.iter().map(|part| part.split_size).collect(); + + let mut split_geom = Vec::new(); + let (mut current_position, split_dimension_space, mut inherited_dimension) = + match layout.direction { + SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), + SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), + }; - for (&size, part) in sizes.iter().zip(&layout.parts) { - let split_dimension = match size { - Some(SplitSize::Percent(percent)) => Dimension::percent(percent), - Some(SplitSize::Fixed(size)) => Dimension::fixed(size), - None => { - let free_percent = if let Some(p) = split_dimension_space.as_percent() { - p - sizes + let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); + + for (&size, part) in sizes.iter().zip(&parts) { + let split_dimension = match size { + Some(SplitSize::Percent(percent)) => Dimension::percent(percent), + Some(SplitSize::Fixed(size)) => Dimension::fixed(size), + None => { + let free_percent = if let Some(p) = split_dimension_space.as_percent() { + p - sizes + .iter() + .map(|&s| { + if let Some(SplitSize::Percent(ip)) = s { + ip + } else { + 0.0 + } + }) + .sum::() + } else { + panic!("Implicit sizing within fixed-size panes is not supported"); + }; + Dimension::percent(free_percent / flex_parts as f64) + }, + }; + inherited_dimension.set_inner( + parts .iter() - .map(|&s| { - if let Some(SplitSize::Percent(ip)) = s { - ip - } else { - 0.0 - } - }) - .sum::() - } else { - panic!("Implicit sizing within fixed-size panes is not supported"); + .map(|p| layout_size(!layout.direction, p)) + .max() + .unwrap(), + ); + let geom = match layout.direction { + SplitDirection::Vertical => PaneGeom { + x: current_position, + y: space_to_split.y, + cols: split_dimension, + rows: inherited_dimension, + }, + SplitDirection::Horizontal => PaneGeom { + x: space_to_split.x, + y: current_position, + cols: inherited_dimension, + rows: split_dimension, + }, }; - Dimension::percent(free_percent / flex_parts as f64) - }, - }; - inherited_dimension.set_inner( - layout - .parts - .iter() - .map(|p| layout_size(!layout.direction, p)) - .max() - .unwrap(), - ); - let geom = match layout.direction { - Direction::Vertical => PaneGeom { - x: current_position, - y: space_to_split.y, - cols: split_dimension, - rows: inherited_dimension, - }, - Direction::Horizontal => PaneGeom { - x: space_to_split.x, - y: current_position, - cols: inherited_dimension, - rows: split_dimension, - }, - }; - split_geom.push(geom); - current_position += layout_size(layout.direction, part); - } + split_geom.push(geom); + current_position += layout_size(layout.direction, part); + } - for (i, part) in layout.parts.iter().enumerate() { - let part_position_and_size = split_geom.get(i).unwrap(); - if !part.parts.is_empty() { - let mut part_positions = split_space(part_position_and_size, part); - pane_positions.append(&mut part_positions); - } else { - pane_positions.push((part.clone(), *part_position_and_size)); + for (i, part) in parts.iter().enumerate() { + let part_position_and_size = split_geom.get(i).unwrap(); + if !part.parts.is_empty() { + let mut part_positions = split_space(part_position_and_size, part); + pane_positions.append(&mut part_positions); + } else { + pane_positions.push((part.clone(), *part_position_and_size)); + } + } + pane_positions + }, + LayoutParts::Tabs(tabs) => { + // TODO + unimplemented!() } } - pane_positions +// let mut pane_positions = Vec::new(); +// // let sizes: Vec> = layout.parts.iter().map(|part| part.split_size).collect(); +// let sizes: Vec> = match layout.parts { +// LayoutParts::Panes(parts) => parts.iter().map(|part| part.split_size).collect(), +// LayoutParts::Tabs(tabs) => tabs.values().map(|part| part.split_size).collect(), +// }; +// +// let mut split_geom = Vec::new(); +// let (mut current_position, split_dimension_space, mut inherited_dimension) = +// match layout.direction { +// SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), +// SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), +// }; +// +// let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); +// +// let parts = match layout.parts { +// LayoutParts::Panes(parts) +// } +// for (&size, part) in sizes.iter().zip(&layout.parts) { +// let split_dimension = match size { +// Some(SplitSize::Percent(percent)) => Dimension::percent(percent), +// Some(SplitSize::Fixed(size)) => Dimension::fixed(size), +// None => { +// let free_percent = if let Some(p) = split_dimension_space.as_percent() { +// p - sizes +// .iter() +// .map(|&s| { +// if let Some(SplitSize::Percent(ip)) = s { +// ip +// } else { +// 0.0 +// } +// }) +// .sum::() +// } else { +// panic!("Implicit sizing within fixed-size panes is not supported"); +// }; +// Dimension::percent(free_percent / flex_parts as f64) +// }, +// }; +// inherited_dimension.set_inner( +// layout +// .parts +// .iter() +// .map(|p| layout_size(!layout.direction, p)) +// .max() +// .unwrap(), +// ); +// let geom = match layout.direction { +// SplitDirection::Vertical => PaneGeom { +// x: current_position, +// y: space_to_split.y, +// cols: split_dimension, +// rows: inherited_dimension, +// }, +// SplitDirection::Horizontal => PaneGeom { +// x: space_to_split.x, +// y: current_position, +// cols: inherited_dimension, +// rows: split_dimension, +// }, +// }; +// split_geom.push(geom); +// current_position += layout_size(layout.direction, part); +// } +// +// for (i, part) in layout.parts.iter().enumerate() { +// let part_position_and_size = split_geom.get(i).unwrap(); +// if !part.parts.is_empty() { +// let mut part_positions = split_space(part_position_and_size, part); +// pane_positions.append(&mut part_positions); +// } else { +// pane_positions.push((part.clone(), *part_position_and_size)); +// } +// } +// pane_positions } impl TryFrom for RunPluginLocation { @@ -739,10 +1165,11 @@ impl TryFrom for Layout { direction: tab.direction, pane_name: tab.pane_name, borderless: tab.borderless, - parts: Self::from_vec_tab_layout(tab.parts)?, + parts: LayoutParts::Panes(Self::from_vec_tab_layout(tab.parts)?), split_size: tab.split_size, focus: tab.focus, run: tab.run.map(Run::try_from).transpose()?, + is_body: false, }) } } @@ -770,7 +1197,7 @@ impl TryFrom for Layout { direction: template.direction, pane_name: template.pane_name, borderless: template.borderless, - parts: Self::from_vec_template_layout(template.parts)?, + parts: LayoutParts::Panes(Self::from_vec_template_layout(template.parts)?), split_size: template.split_size, focus: template.focus, run: template @@ -779,6 +1206,7 @@ impl TryFrom for Layout { // FIXME: This is just Result::transpose but that method is unstable, when it // stabalizes we should swap this out. .map_or(Ok(None), |r| r.map(Some))?, + is_body: false, }) } } @@ -786,7 +1214,7 @@ impl TryFrom for Layout { impl Default for TabLayout { fn default() -> Self { Self { - direction: Direction::Horizontal, + direction: SplitDirection::Horizontal, borderless: false, parts: vec![], split_size: None, @@ -801,12 +1229,12 @@ impl Default for TabLayout { impl Default for LayoutTemplate { fn default() -> Self { Self { - direction: Direction::Horizontal, + direction: SplitDirection::Horizontal, pane_name: None, body: false, borderless: false, parts: vec![LayoutTemplate { - direction: Direction::Horizontal, + direction: SplitDirection::Horizontal, pane_name: None, body: true, borderless: false, @@ -822,9 +1250,34 @@ impl Default for LayoutTemplate { } } -impl Default for Direction { +impl Default for SplitDirection { fn default() -> Self { - Direction::Horizontal + SplitDirection::Horizontal + } +} + +impl FromStr for SplitDirection { + type Err = Box; + fn from_str(s: &str) -> Result { + match s { + "vertical" | "Vertical" => Ok(SplitDirection::Vertical), + "horizontal" | "Horizontal" => Ok(SplitDirection::Horizontal), + _ => Err("split direction must be either vertical or horizontal".into()), + } + } +} + +impl FromStr for SplitSize { + type Err = Box; + fn from_str(s: &str) -> Result { + if s.chars().last() == Some('%') { + let char_count = s.chars().count(); + let percent_size = usize::from_str_radix(&s[..char_count], 10)?; + Ok(SplitSize::Percent(percent_size as f64)) + } else { + let fixed_size = usize::from_str_radix(s, 10)?; + Ok(SplitSize::Fixed(fixed_size)) + } } } diff --git a/zellij-utils/src/ipc.rs b/zellij-utils/src/ipc.rs index a526897987..b60c7fddef 100644 --- a/zellij-utils/src/ipc.rs +++ b/zellij-utils/src/ipc.rs @@ -4,7 +4,7 @@ use crate::{ cli::CliArgs, data::{ClientId, InputMode, Style}, errors::{get_current_ctx, ErrorContext}, - input::{actions::Action, layout::LayoutFromYaml, options::Options, plugins::PluginsConfig}, + input::{actions::Action, layout::{Layout, LayoutFromYaml}, options::Options, plugins::PluginsConfig}, pane_size::{Size, SizeInPixels}, }; use interprocess::local_socket::LocalSocketStream; @@ -84,7 +84,8 @@ pub enum ClientToServerMsg { ClientAttributes, Box, Box, - Box, + // Box, + Box, Option, ), AttachClient(ClientAttributes, Options), diff --git a/zellij-utils/src/kdl.rs b/zellij-utils/src/kdl.rs index 5b0d575c9f..4d4026cd1f 100644 --- a/zellij-utils/src/kdl.rs +++ b/zellij-utils/src/kdl.rs @@ -131,6 +131,13 @@ macro_rules! kdl_children_nodes_or_error { } } +#[macro_export] +macro_rules! kdl_children_nodes { + ( $kdl_node:expr ) => { + $kdl_node.children().map(|c| c.nodes()) + } +} + #[macro_export] macro_rules! kdl_children_or_error { ( $kdl_node:expr, $error:expr ) => { @@ -167,6 +174,13 @@ macro_rules! kdl_name { } } +#[macro_export] +macro_rules! kdl_document_name { + ( $kdl_node:expr ) => { + $kdl_node.node().name().value() + } +} + #[macro_export] macro_rules! keys_from_kdl { ( $kdl_node:expr ) => { @@ -381,7 +395,7 @@ impl TryFrom<&KdlNode> for Action { "DumpScreen" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "NewPane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "PaneNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), - "NewTab" => Ok(Action::NewTab(None)), // TODO: consider the Some(TabLayout) case... + "NewTab" => Ok(Action::NewTab(None, None)), // TODO: consider the Some(TabLayout, "tab_name") case... "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "Run" => { @@ -517,6 +531,13 @@ macro_rules! kdl_property_first_arg_as_string { } } +#[macro_export] +macro_rules! kdl_has_string_argument { + ( $kdl_node:expr, $string_argument:expr ) => { + $kdl_node.entries().iter().find(|e| e.value().as_string() == Some($string_argument)).is_some() + } +} + #[macro_export] macro_rules! kdl_children_property_first_arg_as_string { ( $kdl_node:expr, $property_name:expr ) => { @@ -563,6 +584,43 @@ macro_rules! kdl_get_child { } } +#[macro_export] +macro_rules! kdl_get_child_entry_bool_value { + ( $kdl_node:expr, $child_name:expr ) => { + $kdl_node.children() + .and_then(|c| c.get($child_name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_bool()) + } +} + +#[macro_export] +macro_rules! kdl_get_child_entry_string_value { + ( $kdl_node:expr, $child_name:expr ) => { + $kdl_node.children() + .and_then(|c| c.get($child_name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_string()) + } +} + +#[macro_export] +macro_rules! kdl_get_string_entry { + ( $kdl_node:expr, $entry_name:expr ) => { + $kdl_node.get($entry_name) + .and_then(|e| e.value().as_string()) + } +} + +#[macro_export] +macro_rules! kdl_get_int_entry { + ( $kdl_node:expr, $entry_name:expr ) => { + $kdl_node.get($entry_name) + .and_then(|e| e.value().as_i64()) + } +} + + impl Options { pub fn from_kdl(kdl_options: &KdlDocument) -> Self { let on_force_close = kdl_property_first_arg_as_string!(kdl_options, "on_force_close") diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index 9fac91b706..c903a53926 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -6,7 +6,7 @@ use crate::{ }, input::{ config::{Config, ConfigError}, - layout::{LayoutFromYaml, LayoutFromYamlIntermediate}, + layout::{Layout, LayoutFromYaml, LayoutFromYamlIntermediate}, options::Options, }, }; @@ -19,7 +19,7 @@ use std::{ }; const CONFIG_LOCATION: &str = ".config/zellij"; -const CONFIG_NAME: &str = "config.yaml"; +const CONFIG_NAME: &str = "config.kdl"; static ARROW_SEPARATOR: &str = ""; #[cfg(not(test))] @@ -94,25 +94,25 @@ pub const DEFAULT_CONFIG: &[u8] = include_bytes!(concat!( pub const DEFAULT_LAYOUT: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", - "assets/layouts/default.yaml" + "assets/layouts/default.kdl" )); pub const STRIDER_LAYOUT: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", - "assets/layouts/strider.yaml" + "assets/layouts/strider.kdl" )); pub const NO_STATUS_LAYOUT: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", - "assets/layouts/disable-status-bar.yaml" + "assets/layouts/disable-status-bar.kdl" )); pub const COMPACT_BAR_LAYOUT: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", - "assets/layouts/compact.yaml" + "assets/layouts/compact.kdl" )); pub const FISH_EXTRA_COMPLETION: &[u8] = include_bytes!(concat!( @@ -196,7 +196,8 @@ impl Setup { /// 3. config options (`config.yaml`) pub fn from_options( opts: &CliArgs, - ) -> Result<(Config, Option, Options), ConfigError> { + // ) -> Result<(Config, Option, Options), ConfigError> { + ) -> Result<(Config, Layout, Options), ConfigError> { let clean = match &opts.command { Some(Command::Setup(ref setup)) => setup.clean, _ => false, @@ -234,15 +235,19 @@ impl Setup { .layout .clone() .or_else(|| config_options.default_layout.clone()); - let layout_result = - LayoutFromYamlIntermediate::from_path_or_default(chosen_layout.as_ref(), layout_dir); - let layout = match layout_result { - None => None, - Some(Ok(layout)) => Some(layout), - Some(Err(e)) => { - return Err(e); - }, - }; + +// // TODO: CONTINUE HERE - change this to Layout::from_kdl and have this function return a +// // Layout instead of a LayoutFromYaml +// let layout_result = +// LayoutFromYamlIntermediate::from_path_or_default(chosen_layout.as_ref(), layout_dir); +// let layout = match layout_result { +// None => None, +// Some(Ok(layout)) => Some(layout), +// Some(Err(e)) => { +// return Err(e); +// }, +// }; + let layout = Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir)?; if let Some(Command::Setup(ref setup)) = &opts.command { setup @@ -305,26 +310,29 @@ impl Setup { fn merge_config_with_layout( config: Config, - layout: Option, + // layout: Option, + layout: Layout, config_options: Options, - ) -> Result<(Config, Option, Options), ConfigError> { - let (layout, layout_config) = match layout.map(|l| l.to_layout_and_config()) { - None => (None, None), - Some((layout, layout_config)) => (Some(layout), layout_config), - }; - - let (config, config_options) = if let Some(layout_config) = layout_config { - let config_options = if let Some(options) = layout_config.options.clone() { - config_options.merge(options) - } else { - config_options - }; - // let config = config.merge(layout_config.try_into()?); // TODO: NO! handle this! - (config, config_options) - } else { - (config, config_options) - }; - Ok((config, layout, config_options)) + // ) -> Result<(Config, Option, Options), ConfigError> { + ) -> Result<(Config, Layout, Options), ConfigError> { + unimplemented!() +// let (layout, layout_config) = match layout.map(|l| l.to_layout_and_config()) { +// None => (None, None), +// Some((layout, layout_config)) => (Some(layout), layout_config), +// }; +// +// let (config, config_options) = if let Some(layout_config) = layout_config { +// let config_options = if let Some(options) = layout_config.options.clone() { +// config_options.merge(options) +// } else { +// config_options +// }; +// // let config = config.merge(layout_config.try_into()?); // TODO: NO! handle this! +// (config, config_options) +// } else { +// (config, config_options) +// }; +// Ok((config, layout, config_options)) } pub fn check_defaults_config(opts: &CliArgs, config_options: &Options) -> std::io::Result<()> { From 921dfccdfc203f59ed3fd562a8c6ad1f423fa208 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 15 Aug 2022 12:12:52 +0200 Subject: [PATCH 06/55] tab merge layout --- zellij-server/src/os_input_output.rs | 1 + zellij-server/src/panes/tiled_panes/mod.rs | 24 +-- .../src/panes/tiled_panes/pane_resizer.rs | 22 +-- .../src/panes/tiled_panes/tiled_pane_grid.rs | 70 ++++----- zellij-server/src/pty.rs | 9 +- zellij-utils/src/input/layout.rs | 137 ++++++++++-------- zellij-utils/src/setup.rs | 6 +- 7 files changed, 147 insertions(+), 122 deletions(-) diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs index 31f1780b8f..a2bd840f11 100644 --- a/zellij-server/src/os_input_output.rs +++ b/zellij-server/src/os_input_output.rs @@ -374,6 +374,7 @@ impl ServerOsApi for ServerOsInputOutput { Ok(()) } fn send_to_client(&self, client_id: ClientId, msg: ServerToClientMsg) { + log::info!("sending message to client: {:?}", msg); if let Some(sender) = self.client_senders.lock().unwrap().get_mut(&client_id) { sender.send(msg); } diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 03fa0c0634..0358fc762a 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -14,7 +14,7 @@ use std::rc::Rc; use std::time::Instant; use zellij_utils::{ data::{ModeInfo, Style}, - input::layout::Direction, + input::layout::SplitDirection, pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, }; @@ -211,7 +211,7 @@ impl TiledPanes { pub fn pane_ids(&self) -> impl Iterator { self.panes.keys() } - pub fn relayout(&mut self, direction: Direction) { + pub fn relayout(&mut self, direction: SplitDirection) { let mut pane_grid = TiledPaneGrid::new( &mut self.panes, &self.panes_to_hide, @@ -219,10 +219,10 @@ impl TiledPanes { *self.viewport.borrow(), ); let result = match direction { - Direction::Horizontal => { + SplitDirection::Horizontal => { pane_grid.layout(direction, (*self.display_area.borrow()).cols) }, - Direction::Vertical => pane_grid.layout(direction, (*self.display_area.borrow()).rows), + SplitDirection::Vertical => pane_grid.layout(direction, (*self.display_area.borrow()).rows), }; if let Err(e) = &result { log::error!("{:?} relayout of the tab failed: {}", direction, e); @@ -269,7 +269,7 @@ impl TiledPanes { if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 { return false; } else { - return split(Direction::Horizontal, &full_pane_size).is_some(); + return split(SplitDirection::Horizontal, &full_pane_size).is_some(); } } } @@ -282,7 +282,7 @@ impl TiledPanes { if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 { return false; } - return split(Direction::Vertical, &full_pane_size).is_some(); + return split(SplitDirection::Vertical, &full_pane_size).is_some(); } } false @@ -296,11 +296,11 @@ impl TiledPanes { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); let active_pane = self.panes.get_mut(active_pane_id).unwrap(); let full_pane_size = active_pane.position_and_size(); - if let Some((top_winsize, bottom_winsize)) = split(Direction::Horizontal, &full_pane_size) { + if let Some((top_winsize, bottom_winsize)) = split(SplitDirection::Horizontal, &full_pane_size) { active_pane.set_geom(top_winsize); new_pane.set_geom(bottom_winsize); self.panes.insert(pid, new_pane); - self.relayout(Direction::Vertical); + self.relayout(SplitDirection::Vertical); } } pub fn split_pane_vertically( @@ -312,11 +312,11 @@ impl TiledPanes { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); let active_pane = self.panes.get_mut(active_pane_id).unwrap(); let full_pane_size = active_pane.position_and_size(); - if let Some((left_winsize, right_winsize)) = split(Direction::Vertical, &full_pane_size) { + if let Some((left_winsize, right_winsize)) = split(SplitDirection::Vertical, &full_pane_size) { active_pane.set_geom(left_winsize); new_pane.set_geom(right_winsize); self.panes.insert(pid, new_pane); - self.relayout(Direction::Horizontal); + self.relayout(SplitDirection::Horizontal); } } pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) { @@ -455,7 +455,7 @@ impl TiledPanes { *display_area, *viewport, ); - if pane_grid.layout(Direction::Horizontal, cols).is_ok() { + if pane_grid.layout(SplitDirection::Horizontal, cols).is_ok() { let column_difference = cols as isize - display_area.cols as isize; // FIXME: Should the viewport be an Offset? viewport.cols = (viewport.cols as isize + column_difference) as usize; @@ -463,7 +463,7 @@ impl TiledPanes { } else { log::error!("Failed to horizontally resize the tab!!!"); } - if pane_grid.layout(Direction::Vertical, rows).is_ok() { + if pane_grid.layout(SplitDirection::Vertical, rows).is_ok() { let row_difference = rows as isize - display_area.rows as isize; viewport.rows = (viewport.rows as isize + row_difference) as usize; display_area.rows = rows; diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index 6819a0a800..8667e2f6f8 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -8,7 +8,7 @@ use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::rc::Rc; use zellij_utils::{ - input::layout::Direction, + input::layout::SplitDirection, pane_size::{Constraint, Dimension, PaneGeom}, }; @@ -23,7 +23,7 @@ pub struct PaneResizer<'a> { #[derive(Debug, Clone, Copy)] struct Span { pid: PaneId, - direction: Direction, + direction: SplitDirection, pos: usize, size: Dimension, size_var: Variable, @@ -44,7 +44,7 @@ impl<'a> PaneResizer<'a> { } } - pub fn layout(&mut self, direction: Direction, space: usize) -> Result<(), String> { + pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<(), String> { self.solver.reset(); let grid = self.solve(direction, space)?; let spans = self.discretize_spans(grid, space)?; @@ -52,7 +52,7 @@ impl<'a> PaneResizer<'a> { Ok(()) } - fn solve(&mut self, direction: Direction, space: usize) -> Result { + fn solve(&mut self, direction: SplitDirection, space: usize) -> Result { let grid: Grid = self .grid_boundaries(direction) .into_iter() @@ -127,12 +127,12 @@ impl<'a> PaneResizer<'a> { for span in spans { let pane = panes.get_mut(&span.pid).unwrap(); let new_geom = match span.direction { - Direction::Horizontal => PaneGeom { + SplitDirection::Horizontal => PaneGeom { x: span.pos, cols: span.size, ..pane.current_geom() }, - Direction::Vertical => PaneGeom { + SplitDirection::Vertical => PaneGeom { y: span.pos, rows: span.size, ..pane.current_geom() @@ -147,7 +147,7 @@ impl<'a> PaneResizer<'a> { } // FIXME: Functions like this should have unit tests! - fn grid_boundaries(&self, direction: Direction) -> Vec<(usize, usize)> { + fn grid_boundaries(&self, direction: SplitDirection) -> Vec<(usize, usize)> { // Select the spans running *perpendicular* to the direction of resize let spans: Vec = self .panes @@ -169,7 +169,7 @@ impl<'a> PaneResizer<'a> { bounds } - fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec { + fn spans_in_boundary(&self, direction: SplitDirection, boundary: (usize, usize)) -> Vec { let bwn = |v, (s, e)| s <= v && v < e; let mut spans: Vec<_> = self .panes @@ -188,19 +188,19 @@ impl<'a> PaneResizer<'a> { spans } - fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span { + fn get_span(&self, direction: SplitDirection, pane: &dyn Pane) -> Span { let pas = pane.current_geom(); // let size_var = self.vars[&pane.pid()]; let size_var = *self.vars.get(&pane.pid()).unwrap(); match direction { - Direction::Horizontal => Span { + SplitDirection::Horizontal => Span { pid: pane.pid(), direction, pos: pas.x, size: pas.cols, size_var, }, - Direction::Vertical => Span { + SplitDirection::Vertical => Span { pid: pane.pid(), direction, pos: pas.y, diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs index 6bb90e9d37..7fc407feec 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -5,7 +5,7 @@ use crate::{panes::PaneId, tab::Pane}; use std::cmp::Reverse; use std::collections::{HashMap, HashSet}; use zellij_utils::{ - input::layout::Direction, + input::layout::SplitDirection, pane_size::{Dimension, PaneGeom, Size, Viewport}, }; @@ -42,7 +42,7 @@ impl<'a> TiledPaneGrid<'a> { } } - pub fn layout(&mut self, direction: Direction, space: usize) -> Result<(), String> { + pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<(), String> { let mut pane_resizer = PaneResizer::new(self.panes.clone()); pane_resizer.layout(direction, space) } @@ -187,7 +187,7 @@ impl<'a> TiledPaneGrid<'a> { } fn can_reduce_pane_and_surroundings_right(&self, pane_id: &PaneId, reduce_by: f64) -> bool { let ids_left = self.pane_ids_directly_left_of(pane_id); - let flexible_left = self.ids_are_flexible(Direction::Horizontal, ids_left); + let flexible_left = self.ids_are_flexible(SplitDirection::Horizontal, ids_left); if flexible_left { self.can_reduce_pane_width(pane_id, reduce_by) } else { @@ -196,7 +196,7 @@ impl<'a> TiledPaneGrid<'a> { } fn can_reduce_pane_and_surroundings_left(&self, pane_id: &PaneId, reduce_by: f64) -> bool { let ids_right = self.pane_ids_directly_right_of(pane_id); - let flexible_right = self.ids_are_flexible(Direction::Horizontal, ids_right); + let flexible_right = self.ids_are_flexible(SplitDirection::Horizontal, ids_right); if flexible_right { self.can_reduce_pane_width(pane_id, reduce_by) } else { @@ -205,7 +205,7 @@ impl<'a> TiledPaneGrid<'a> { } fn can_reduce_pane_and_surroundings_down(&self, pane_id: &PaneId, reduce_by: f64) -> bool { let ids_above = self.pane_ids_directly_above(pane_id); - let flexible_above = self.ids_are_flexible(Direction::Vertical, ids_above); + let flexible_above = self.ids_are_flexible(SplitDirection::Vertical, ids_above); if flexible_above { self.can_reduce_pane_height(pane_id, reduce_by) } else { @@ -214,7 +214,7 @@ impl<'a> TiledPaneGrid<'a> { } fn can_reduce_pane_and_surroundings_up(&self, pane_id: &PaneId, reduce_by: f64) -> bool { let ids_below = self.pane_ids_directly_below(pane_id); - let flexible_below = self.ids_are_flexible(Direction::Vertical, ids_below); + let flexible_below = self.ids_are_flexible(SplitDirection::Vertical, ids_below); if flexible_below { self.can_reduce_pane_height(pane_id, reduce_by) } else { @@ -926,15 +926,15 @@ impl<'a> TiledPaneGrid<'a> { let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); (right_resize_border, pane_ids) } - fn ids_are_flexible(&self, direction: Direction, pane_ids: Option>) -> bool { + fn ids_are_flexible(&self, direction: SplitDirection, pane_ids: Option>) -> bool { let panes = self.panes.borrow(); pane_ids.is_some() && pane_ids.unwrap().iter().all(|id| { let pane_to_check = panes.get(id).unwrap(); let geom = pane_to_check.current_geom(); let dimension = match direction { - Direction::Vertical => geom.rows, - Direction::Horizontal => geom.cols, + SplitDirection::Vertical => geom.rows, + SplitDirection::Horizontal => geom.cols, }; !dimension.is_fixed() }) @@ -967,7 +967,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_increase_pane_and_surroundings_right(pane_id, reduce_by) { self.increase_pane_and_surroundings_right(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); return true; } false @@ -980,7 +980,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_increase_pane_and_surroundings_left(pane_id, reduce_by) { self.increase_pane_and_surroundings_left(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); return true; } false @@ -989,7 +989,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_increase_pane_and_surroundings_up(pane_id, reduce_by) { self.increase_pane_and_surroundings_up(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); return true; } false @@ -1002,7 +1002,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_increase_pane_and_surroundings_down(pane_id, reduce_by) { self.increase_pane_and_surroundings_down(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); return true; } false @@ -1245,7 +1245,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_reduce_pane_and_surroundings_right(pane_id, reduce_by) { self.reduce_pane_and_surroundings_right(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); return true; } false @@ -1254,7 +1254,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_reduce_pane_and_surroundings_left(pane_id, reduce_by) { self.reduce_pane_and_surroundings_left(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); return true; } false @@ -1263,7 +1263,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_reduce_pane_and_surroundings_up(pane_id, reduce_by) { self.reduce_pane_and_surroundings_up(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); return true; } false @@ -1272,7 +1272,7 @@ impl<'a> TiledPaneGrid<'a> { if self.can_reduce_pane_and_surroundings_down(pane_id, reduce_by) { self.reduce_pane_and_surroundings_down(pane_id, reduce_by); let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); return true; } false @@ -1551,31 +1551,31 @@ impl<'a> TiledPaneGrid<'a> { } None } - fn find_panes_to_grow(&self, id: PaneId) -> Option<(Vec, Direction)> { + fn find_panes_to_grow(&self, id: PaneId) -> Option<(Vec, SplitDirection)> { if let Some(panes) = self .panes_to_the_left_between_aligning_borders(id) .or_else(|| self.panes_to_the_right_between_aligning_borders(id)) { - return Some((panes, Direction::Horizontal)); + return Some((panes, SplitDirection::Horizontal)); } if let Some(panes) = self .panes_above_between_aligning_borders(id) .or_else(|| self.panes_below_between_aligning_borders(id)) { - return Some((panes, Direction::Vertical)); + return Some((panes, SplitDirection::Vertical)); } None } - fn grow_panes(&mut self, panes: &[PaneId], direction: Direction, (width, height): (f64, f64)) { + fn grow_panes(&mut self, panes: &[PaneId], direction: SplitDirection, (width, height): (f64, f64)) { match direction { - Direction::Horizontal => { + SplitDirection::Horizontal => { for pane_id in panes { self.increase_pane_width(pane_id, width); } }, - Direction::Vertical => { + SplitDirection::Vertical => { for pane_id in panes { self.increase_pane_height(pane_id, height); } @@ -1597,8 +1597,8 @@ impl<'a> TiledPaneGrid<'a> { if let Some((panes_to_grow, direction)) = self.find_panes_to_grow(id) { self.grow_panes(&panes_to_grow, direction, (freed_width, freed_height)); let side_length = match direction { - Direction::Vertical => self.display_area.rows, - Direction::Horizontal => self.display_area.cols, + SplitDirection::Vertical => self.display_area.rows, + SplitDirection::Horizontal => self.display_area.cols, }; { let mut panes = self.panes.borrow_mut(); @@ -1614,7 +1614,7 @@ impl<'a> TiledPaneGrid<'a> { pub fn find_room_for_new_pane( &self, cursor_height_width_ratio: Option, - ) -> Option<(PaneId, Direction)> { + ) -> Option<(PaneId, SplitDirection)> { let panes = self.panes.borrow(); let pane_sequence: Vec<(&PaneId, &&mut Box)> = panes.iter().filter(|(_, p)| p.selectable()).collect(); @@ -1643,9 +1643,9 @@ impl<'a> TiledPaneGrid<'a> { > pane_to_split.cols() && pane_to_split.rows() > pane_to_split.min_height() * 2 { - Some(Direction::Horizontal) + Some(SplitDirection::Horizontal) } else if pane_to_split.cols() > pane_to_split.min_width() * 2 { - Some(Direction::Vertical) + Some(SplitDirection::Vertical) } else { None }; @@ -1655,29 +1655,29 @@ impl<'a> TiledPaneGrid<'a> { } } -pub fn split(direction: Direction, rect: &PaneGeom) -> Option<(PaneGeom, PaneGeom)> { +pub fn split(direction: SplitDirection, rect: &PaneGeom) -> Option<(PaneGeom, PaneGeom)> { let space = match direction { - Direction::Vertical => rect.cols, - Direction::Horizontal => rect.rows, + SplitDirection::Vertical => rect.cols, + SplitDirection::Horizontal => rect.rows, }; if let Some(p) = space.as_percent() { let first_rect = match direction { - Direction::Vertical => PaneGeom { + SplitDirection::Vertical => PaneGeom { cols: Dimension::percent(p / 2.0), ..*rect }, - Direction::Horizontal => PaneGeom { + SplitDirection::Horizontal => PaneGeom { rows: Dimension::percent(p / 2.0), ..*rect }, }; let second_rect = match direction { - Direction::Vertical => PaneGeom { + SplitDirection::Vertical => PaneGeom { x: first_rect.x + 1, cols: first_rect.cols, ..*rect }, - Direction::Horizontal => PaneGeom { + SplitDirection::Horizontal => PaneGeom { y: first_rect.y + 1, rows: first_rect.rows, ..*rect diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index fc7843509e..62160b09a1 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -148,11 +148,12 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { // }); // let merged_layout = layout.template.clone().insert_tab_layout(tab_layout); - let merged_layout = tab_layout.map(|t| layout.insert_tab_layout(t)).unwrap_or_else(|| *layout.clone()); - let layout: Layout = - Layout::try_from(merged_layout).unwrap_or_else(|err| panic!("{}", err)); + let mut layout = layout.clone(); + if let Some(tab_layout) = tab_layout { + layout.insert_tab_layout(&tab_layout).expect("corrupted tab layout"); + } - pty.spawn_terminals_for_layout(layout, terminal_action.clone(), client_id); + pty.spawn_terminals_for_layout(*layout, terminal_action.clone(), client_id); if let Some(tab_name) = tab_name { // clear current name at first diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 014b04e744..157fb6a587 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -164,7 +164,6 @@ impl fmt::Display for RunPluginLocation { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] #[serde(crate = "self::serde")] pub struct Layout { - pub is_body: bool, pub direction: SplitDirection, #[serde(default)] pub pane_name: Option, @@ -175,11 +174,12 @@ pub struct Layout { #[serde(default)] pub borderless: bool, pub focus: Option, + pub tabs_index_in_children: Option, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum LayoutParts { - Tabs(HashMap), // String is the tab name + Tabs(Vec<(String, Layout)>), // String is the tab name Panes(Vec), } @@ -641,25 +641,27 @@ impl Layout { run = Some(Run::Plugin(RunPlugin::from_kdl(kdl_plugin_block)?)); } let mut layout_parts = vec![]; - let mut is_body = false; + let mut tabs_index_in_children = None; if let Some(kdl_parts) = kdl_get_child!(kdl_layout, "parts") { let direction = kdl_get_string_entry!(kdl_parts, "direction").ok_or(ConfigError::KdlParsingError("no direction found for layout part".into()))?; let direction = SplitDirection::from_str(direction)?; // let mut parts: Vec = vec![]; - if let Some(children) = kdl_children_nodes!(kdl_layout) { - for child in children { + // if let Some(children) = kdl_children_nodes!(kdl_layout) { + if let Some(children) = kdl_children_nodes!(kdl_parts) { + for (i, child) in children.iter().enumerate() { let child_name = kdl_name!(child); if child_name == "layout" { layout_parts.push(parse_kdl_layout(&child, Some(direction), tabs)?); } else if child_name == "tabs" { - is_body = true; + tabs_index_in_children = Some(i); if !tabs.is_empty() { return Err(ConfigError::KdlParsingError(format!("Only one 'tabs' section allowed per layout..."))); } match kdl_children_nodes!(child) { Some(children) => { for child in children { - tabs.push(parse_kdl_layout(&child, Some(direction), tabs)?); + let tab_layout = parse_kdl_layout(&child, Some(direction), tabs)?; + tabs.push(tab_layout); } }, @@ -671,7 +673,6 @@ impl Layout { } } } - // let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; Ok(Layout { direction, pane_name: None, // TODO @@ -680,35 +681,26 @@ impl Layout { run, borderless, focus: None, // TODO - is_body, + tabs_index_in_children, }) } - let mut base_layout = parse_kdl_layout(layout_node, direction, &mut tabs); - let mut parts: Vec = vec![]; - for tab in tabs { - // TODO: CONTINUE HERE (10/08) populate the LayoutParts::Tabs(tabs) thing we created - // but before that, might want to do a cargo check in zellij-utils to adjust all the - // other stuff (possible in other places too with cargo make check) + let mut base_layout = parse_kdl_layout(layout_node, direction, &mut tabs)?; + if !tabs.is_empty() { + let mut root_layout = Layout::default(); + let mut tab_parts: Vec<(String, Layout)> = vec![]; + for (i, tab) in tabs.drain(..).enumerate() { + let tab_name = format!("{}", i); // TODO: support tab name in layout + let mut layout_for_tab = base_layout.clone(); + layout_for_tab.insert_tab_layout(&tab); + tab_parts.push((tab_name, layout_for_tab)); + + } + root_layout.parts = LayoutParts::Tabs(tab_parts); + Ok(root_layout) + } else { + Ok(base_layout) } - base_layout - - -// // TODO: this is not right, it should be taken from parts -// let direction = kdl_get_string_entry!(layout_template_node, "direction").unwrap_or("horizontal"); -// let direction = SplitDirection::from_str(direction)?; - // TBD - // let tabs_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "tabs"); -// Layout { -// borderless: bool, -// focus: Option, -// pane_name: Option, -// direction: Direction, -// split_size: Option, -// run: Option, -// -// parts: Vec, -// } } // let layout: Option = match serde_yaml::from_str(&layout) { @@ -777,7 +769,7 @@ impl Layout { pub fn total_terminal_panes(&self) -> usize { // TODO: better let mut total_panes = 0; - match self.parts { + match &self.parts { LayoutParts::Panes(parts) => { total_panes += parts.len(); for part in parts { @@ -791,9 +783,10 @@ impl Layout { total_panes }, LayoutParts::Tabs(tabs) => { - let parts = tabs.values(); - total_panes += parts.len(); - for part in parts { + // let parts = tabs.values(); + total_panes += tabs.len(); + for tab in tabs { + let (_tab_name, part) = tab; match part.run { Some(Run::Command(_)) | None => { total_panes += part.total_terminal_panes(); @@ -809,18 +802,19 @@ impl Layout { pub fn total_borderless_panes(&self) -> usize { // TODO: better let mut total_borderless_panes = 0; - match self.parts { + match &self.parts { LayoutParts::Panes(parts) => { total_borderless_panes += parts.iter().filter(|p| p.borderless).count(); - for part in &parts { + for part in parts { total_borderless_panes += part.total_borderless_panes(); } total_borderless_panes }, LayoutParts::Tabs(tabs) => { - let parts = tabs.values(); - total_borderless_panes += parts.filter(|p| p.borderless).count(); - for part in parts { + // let parts = tabs.values(); + total_borderless_panes += tabs.iter().filter(|(_, p)| p.borderless).count(); + for part in tabs { + let (_part_name, part) = part; total_borderless_panes += part.total_borderless_panes(); } total_borderless_panes @@ -830,22 +824,22 @@ impl Layout { pub fn extract_run_instructions(&self) -> Vec> { // TODO: better let mut run_instructions = vec![]; - match self.parts { + match &self.parts { LayoutParts::Panes(parts) => { if parts.is_empty() { run_instructions.push(self.run.clone()); } - for part in &parts { + for part in parts { let mut current_runnables = part.extract_run_instructions(); run_instructions.append(&mut current_runnables); } }, LayoutParts::Tabs(tabs) => { - let parts = tabs.values(); - if parts.len() == 0 { + if tabs.len() == 0 { run_instructions.push(self.run.clone()); } - for part in parts { + for tab in tabs { + let (_part_name, part) = tab; let mut current_runnables = part.extract_run_instructions(); run_instructions.append(&mut current_runnables); } @@ -864,8 +858,35 @@ impl Layout { // self.parts.append(&mut parts); } - pub fn insert_tab_layout(&self, tab_layout: Layout) -> Layout { - unimplemented!() + pub fn insert_tab_layout(&mut self, tab_layout: &Layout) -> Result<(), &'static str> { + match self.tabs_index_in_children { + Some(tabs_index_in_children) => { + match &mut self.parts { + LayoutParts::Panes(panes) => { + panes.insert(tabs_index_in_children, tab_layout.clone()); + Ok(()) + }, + LayoutParts::Tabs(_) => { + Err("Only top layout part can have a tabs block") + } + } + }, + None => { + match &mut self.parts { + LayoutParts::Panes(panes) => { + for child in panes.iter_mut() { + if let Ok(_) = child.insert_tab_layout(tab_layout) { + return Ok(()); + } + } + Err("no place to insert tabs here") + }, + LayoutParts::Tabs(_) => { + Err("Only top layout part can have a tabs block") + } + } + } + } } pub fn has_tabs(&self) -> bool { @@ -904,15 +925,15 @@ fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { layout: &Layout, ) -> usize { let size = if parent_direction == direction { 1 } else { 0 }; - let parts_is_empty = match layout.parts { + let parts_is_empty = match &layout.parts { LayoutParts::Panes(parts) => parts.is_empty(), - LayoutParts::Tabs(tabs) => tabs.values().len() == 0 + LayoutParts::Tabs(tabs) => tabs.len() == 0 }; // if layout.parts.is_empty() { if parts_is_empty { size } else { - match layout.parts { + match &layout.parts { LayoutParts::Panes(parts) => { let children_size = parts .iter() @@ -922,8 +943,8 @@ fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { }, LayoutParts::Tabs(tabs) => { let children_size = tabs - .values() - .map(|p| child_layout_size(direction, layout.direction, p)) + .iter() + .map(|(_, p)| child_layout_size(direction, layout.direction, p)) .sum(); max(size, children_size) } @@ -934,7 +955,7 @@ fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { } fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneGeom)> { - match layout.parts { + match &layout.parts { LayoutParts::Panes(parts) => { let mut pane_positions = Vec::new(); let sizes: Vec> = parts.iter().map(|part| part.split_size).collect(); @@ -948,7 +969,7 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); - for (&size, part) in sizes.iter().zip(&parts) { + for (&size, part) in sizes.iter().zip(&*parts) { let split_dimension = match size { Some(SplitSize::Percent(percent)) => Dimension::percent(percent), Some(SplitSize::Fixed(size)) => Dimension::fixed(size), @@ -1169,7 +1190,7 @@ impl TryFrom for Layout { split_size: tab.split_size, focus: tab.focus, run: tab.run.map(Run::try_from).transpose()?, - is_body: false, + tabs_index_in_children: None, }) } } @@ -1206,7 +1227,7 @@ impl TryFrom for Layout { // FIXME: This is just Result::transpose but that method is unstable, when it // stabalizes we should swap this out. .map_or(Ok(None), |r| r.map(Some))?, - is_body: false, + tabs_index_in_children: None, }) } } diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index c903a53926..45a7b4ada6 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -247,7 +247,9 @@ impl Setup { // return Err(e); // }, // }; + log::info!("building layout"); let layout = Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir)?; + log::info!("built layout: {:?}", layout); if let Some(Command::Setup(ref setup)) = &opts.command { setup @@ -262,8 +264,8 @@ impl Setup { }; // unimplemented!() - // Ok((config, None, config_options)) // TODO: no!!!!!111oneoneone - Setup::merge_config_with_layout(config, layout, config_options) + Ok((config, layout, config_options)) // TODO: no!!!!!111oneoneone + // Setup::merge_config_with_layout(config, layout, config_options) } /// General setup helpers From d3cf00eca4d4cee329578ba6b786b8d5b798c38b Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 15 Aug 2022 21:15:10 +0200 Subject: [PATCH 07/55] work --- zellij-utils/src/input/layout.rs | 2 + zellij-utils/src/input/unit/layout_test.rs | 1691 ++++++++++---------- 2 files changed, 849 insertions(+), 844 deletions(-) diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 24aaf07df8..939b700795 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -887,6 +887,8 @@ impl Layout { } pub fn has_tabs(&self) -> bool { + // TODO: CONTINUE HERE (15/08) - implement these, then test with: + // - cargo make build && target/debug/zellij unimplemented!() } diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 22a10c6fb1..07747573d7 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -13,847 +13,850 @@ fn default_layout_dir(layout: String) -> PathBuf { layout_dir.join(layout) } -#[test] -fn default_layout_is_ok() { - let path = default_layout_dir("default.yaml".into()); - let layout = LayoutFromYaml::new(&path); - assert!(layout.is_ok()); -} - -#[test] -fn default_layout_has_one_tab() { - let path = default_layout_dir("default.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - assert_eq!(layout_template.tabs.len(), 1); -} - -#[test] -fn default_layout_merged_correctly() { - let path = default_layout_dir("default.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[0].clone())); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: true, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false, - })), - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: true, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), - _allow_exec_host_cmd: false, - })), - }, - ], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn default_layout_new_tab_correct() { - let path = default_layout_dir("default.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - let tab_layout = layout_template.template.clone().insert_tab_layout(None); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: true, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false, - })), - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: true, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), - _allow_exec_host_cmd: false, - })), - }, - ], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn default_strider_layout_is_ok() { - let path = default_layout_dir("strider.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - assert!(layout_from_yaml.is_ok()); -} - -#[test] -fn default_disable_status_layout_is_ok() { - let path = default_layout_dir("disable-status-bar.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - assert!(layout_from_yaml.is_ok()); -} - -#[test] -fn default_disable_status_layout_has_no_tab() { - let path = default_layout_dir("disable-status-bar.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - assert_eq!(layout_template.tabs.len(), 0); -} - -#[test] -fn three_panes_with_tab_is_ok() { - let path = layout_test_dir("three-panes-with-tab.yaml".into()); - let layout = LayoutFromYaml::new(&path); - assert!(layout.is_ok()); -} - -#[test] -fn three_panes_with_tab_has_one_tab() { - let path = layout_test_dir("three-panes-with-tab.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.unwrap(); - assert_eq!(layout_template.tabs.len(), 1); -} - -#[test] -fn three_panes_with_tab_merged_correctly() { - let path = layout_test_dir("three-panes-with-tab.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[0].clone())); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - ], - split_size: None, - run: None, - }, - ], - split_size: None, - run: None, - }], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn three_panes_with_tab_new_tab_is_correct() { - let path = layout_test_dir("three-panes-with-tab.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - let tab_layout = layout_template.template.clone().insert_tab_layout(None); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn three_panes_with_tab_and_default_plugins_is_ok() { - let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); - let layout = LayoutFromYaml::new(&path); - assert!(layout.is_ok()); -} - -#[test] -fn three_panes_with_tab_and_default_plugins_has_one_tab() { - let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.unwrap(); - assert_eq!(layout_template.tabs.len(), 1); -} - -#[test] -fn three_panes_with_tab_and_default_plugins_merged_correctly() { - let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[0].clone())); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false, - })), - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - ], - split_size: None, - run: None, - }, - ], - split_size: None, - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), - _allow_exec_host_cmd: false, - })), - }, - ], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { - let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - let tab_layout = layout_template.template.clone().insert_tab_layout(None); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false, - })), - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), - _allow_exec_host_cmd: false, - })), - }, - ], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn deeply_nested_tab_is_ok() { - let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); - let layout = LayoutFromYaml::new(&path); - assert!(layout.is_ok()); -} - -#[test] -fn deeply_nested_tab_has_one_tab() { - let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.unwrap(); - assert_eq!(layout_template.tabs.len(), 1); -} - -#[test] -fn deeply_nested_tab_merged_correctly() { - let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[0].clone())); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(21)), - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(22)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(23)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(24)), - run: None, - }, - ], - split_size: Some(SplitSize::Percent(78)), - run: None, - }, - ], - split_size: Some(SplitSize::Percent(79)), - run: None, - }, - ], - split_size: Some(SplitSize::Percent(90)), - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(15)), - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(15)), - run: None, - }, - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(15)), - run: None, - }, - ], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn three_tabs_is_ok() { - let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - assert!(layout_from_yaml.is_ok()); -} - -#[test] -fn three_tabs_has_three_tabs() { - let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.unwrap(); - assert_eq!(layout_template.tabs.len(), 3); -} - -#[test] -fn three_tabs_tab_one_merged_correctly() { - let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[0].clone())); - let merged_layout = Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - ], - split_size: None, - run: None, - }; - - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn three_tabs_tab_two_merged_correctly() { - let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[1].clone())); - let merged_layout = Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - ], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - ], - split_size: None, - run: None, - }; - - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn three_tabs_tab_three_merged_correctly() { - let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); - let layout = LayoutFromYaml::new(&path); - let layout_template = layout.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[2].clone())); - let merged_layout = Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![ - Layout { - direction: Direction::Vertical, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - ], - split_size: Some(SplitSize::Percent(50)), - run: None, - }, - Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }, - ], - split_size: None, - run: None, - }; - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn no_tabs_is_ok() { - let path = layout_test_dir("no-tab-section-specified.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - assert!(layout_from_yaml.is_ok()); -} - -#[test] -fn no_tabs_has_no_tabs() { - let path = layout_test_dir("no-tab-section-specified.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.unwrap(); - assert_eq!(layout_template.tabs.len(), 0); -} - -#[test] -fn no_tabs_merged_correctly() { - let path = layout_test_dir("no-tab-section-specified.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - let tab_layout = layout_template.template.clone().insert_tab_layout(None); - let merged_layout = Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![Layout { - direction: Direction::Horizontal, - borderless: false, - pane_name: None, - focus: None, - parts: vec![], - split_size: None, - run: None, - }], - split_size: None, - run: None, - }; - - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn no_layout_template_specified_is_ok() { - let path = layout_test_dir("no-layout-template-specified.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - assert!(layout_from_yaml.is_ok()); -} - -#[test] -fn no_layout_template_has_one_tab() { - let path = layout_test_dir("no-layout-template-specified.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.unwrap(); - assert_eq!(layout_template.tabs.len(), 1); -} - -#[test] -fn no_layout_template_merged_correctly() { - let path = layout_test_dir("no-layout-template-specified.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.as_ref().unwrap(); - let tab_layout = layout_template - .template - .clone() - .insert_tab_layout(Some(layout_template.tabs[0].clone())); - let merged_layout = Layout { - direction: Direction::Horizontal, - parts: vec![Layout { - direction: Direction::Vertical, - parts: vec![ - Layout { - direction: Direction::Horizontal, - parts: vec![], - split_size: None, - run: None, - borderless: false, - pane_name: None, - focus: None, - }, - Layout { - direction: Direction::Horizontal, - parts: vec![], - split_size: None, - run: None, - borderless: false, - pane_name: None, - focus: None, - }, - ], - split_size: None, - run: None, - borderless: false, - pane_name: None, - focus: None, - }], - split_size: None, - run: None, - borderless: false, - pane_name: None, - focus: None, - }; - - assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -} - -#[test] -fn session_name_to_layout_is_ok() { - let path = layout_test_dir("session-name-to-layout.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - assert!(layout_from_yaml.is_ok()); -} - -#[test] -fn session_name_to_layout_has_name() { - let path = layout_test_dir("session-name-to-layout.yaml".into()); - let layout_from_yaml = LayoutFromYaml::new(&path); - let layout_template = layout_from_yaml.unwrap(); - let session_layout = layout_template.session; - - let expected_session = SessionFromYaml { - name: Some(String::from("zellij-session")), - attach: Some(true), - }; - - assert_eq!(expected_session, session_layout); -} +// TODO: BRING THESE TESTS BACK!! +// +// +// #[test] +// fn default_layout_is_ok() { +// let path = default_layout_dir("default.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// assert!(layout.is_ok()); +// } +// +// #[test] +// fn default_layout_has_one_tab() { +// let path = default_layout_dir("default.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// assert_eq!(layout_template.tabs.len(), 1); +// } +// +// #[test] +// fn default_layout_merged_correctly() { +// let path = default_layout_dir("default.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: true, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(1)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: true, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(2)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// ], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn default_layout_new_tab_correct() { +// let path = default_layout_dir("default.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: true, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(1)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: true, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(2)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// ], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn default_strider_layout_is_ok() { +// let path = default_layout_dir("strider.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// assert!(layout_from_yaml.is_ok()); +// } +// +// #[test] +// fn default_disable_status_layout_is_ok() { +// let path = default_layout_dir("disable-status-bar.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// assert!(layout_from_yaml.is_ok()); +// } +// +// #[test] +// fn default_disable_status_layout_has_no_tab() { +// let path = default_layout_dir("disable-status-bar.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// assert_eq!(layout_template.tabs.len(), 0); +// } +// +// #[test] +// fn three_panes_with_tab_is_ok() { +// let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// assert!(layout.is_ok()); +// } +// +// #[test] +// fn three_panes_with_tab_has_one_tab() { +// let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.unwrap(); +// assert_eq!(layout_template.tabs.len(), 1); +// } +// +// #[test] +// fn three_panes_with_tab_merged_correctly() { +// let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn three_panes_with_tab_new_tab_is_correct() { +// let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn three_panes_with_tab_and_default_plugins_is_ok() { +// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// assert!(layout.is_ok()); +// } +// +// #[test] +// fn three_panes_with_tab_and_default_plugins_has_one_tab() { +// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.unwrap(); +// assert_eq!(layout_template.tabs.len(), 1); +// } +// +// #[test] +// fn three_panes_with_tab_and_default_plugins_merged_correctly() { +// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(1)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(2)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// ], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { +// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(1)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Fixed(2)), +// run: Some(Run::Plugin(RunPlugin { +// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// _allow_exec_host_cmd: false, +// })), +// }, +// ], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn deeply_nested_tab_is_ok() { +// let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// assert!(layout.is_ok()); +// } +// +// #[test] +// fn deeply_nested_tab_has_one_tab() { +// let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.unwrap(); +// assert_eq!(layout_template.tabs.len(), 1); +// } +// +// #[test] +// fn deeply_nested_tab_merged_correctly() { +// let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(21)), +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(22)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(23)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(24)), +// run: None, +// }, +// ], +// split_size: Some(SplitSize::Percent(78)), +// run: None, +// }, +// ], +// split_size: Some(SplitSize::Percent(79)), +// run: None, +// }, +// ], +// split_size: Some(SplitSize::Percent(90)), +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(15)), +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(15)), +// run: None, +// }, +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(15)), +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn three_tabs_is_ok() { +// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// assert!(layout_from_yaml.is_ok()); +// } +// +// #[test] +// fn three_tabs_has_three_tabs() { +// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.unwrap(); +// assert_eq!(layout_template.tabs.len(), 3); +// } +// +// #[test] +// fn three_tabs_tab_one_merged_correctly() { +// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// let merged_layout = Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }; +// +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn three_tabs_tab_two_merged_correctly() { +// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[1].clone())); +// let merged_layout = Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// ], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }; +// +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn three_tabs_tab_three_merged_correctly() { +// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// let layout = LayoutFromYaml::new(&path); +// let layout_template = layout.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[2].clone())); +// let merged_layout = Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![ +// Layout { +// direction: Direction::Vertical, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// ], +// split_size: Some(SplitSize::Percent(50)), +// run: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }, +// ], +// split_size: None, +// run: None, +// }; +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn no_tabs_is_ok() { +// let path = layout_test_dir("no-tab-section-specified.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// assert!(layout_from_yaml.is_ok()); +// } +// +// #[test] +// fn no_tabs_has_no_tabs() { +// let path = layout_test_dir("no-tab-section-specified.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.unwrap(); +// assert_eq!(layout_template.tabs.len(), 0); +// } +// +// #[test] +// fn no_tabs_merged_correctly() { +// let path = layout_test_dir("no-tab-section-specified.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![Layout { +// direction: Direction::Horizontal, +// borderless: false, +// pane_name: None, +// focus: None, +// parts: vec![], +// split_size: None, +// run: None, +// }], +// split_size: None, +// run: None, +// }; +// +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn no_layout_template_specified_is_ok() { +// let path = layout_test_dir("no-layout-template-specified.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// assert!(layout_from_yaml.is_ok()); +// } +// +// #[test] +// fn no_layout_template_has_one_tab() { +// let path = layout_test_dir("no-layout-template-specified.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.unwrap(); +// assert_eq!(layout_template.tabs.len(), 1); +// } +// +// #[test] +// fn no_layout_template_merged_correctly() { +// let path = layout_test_dir("no-layout-template-specified.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.as_ref().unwrap(); +// let tab_layout = layout_template +// .template +// .clone() +// .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// let merged_layout = Layout { +// direction: Direction::Horizontal, +// parts: vec![Layout { +// direction: Direction::Vertical, +// parts: vec![ +// Layout { +// direction: Direction::Horizontal, +// parts: vec![], +// split_size: None, +// run: None, +// borderless: false, +// pane_name: None, +// focus: None, +// }, +// Layout { +// direction: Direction::Horizontal, +// parts: vec![], +// split_size: None, +// run: None, +// borderless: false, +// pane_name: None, +// focus: None, +// }, +// ], +// split_size: None, +// run: None, +// borderless: false, +// pane_name: None, +// focus: None, +// }], +// split_size: None, +// run: None, +// borderless: false, +// pane_name: None, +// focus: None, +// }; +// +// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// } +// +// #[test] +// fn session_name_to_layout_is_ok() { +// let path = layout_test_dir("session-name-to-layout.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// assert!(layout_from_yaml.is_ok()); +// } +// +// #[test] +// fn session_name_to_layout_has_name() { +// let path = layout_test_dir("session-name-to-layout.yaml".into()); +// let layout_from_yaml = LayoutFromYaml::new(&path); +// let layout_template = layout_from_yaml.unwrap(); +// let session_layout = layout_template.session; +// +// let expected_session = SessionFromYaml { +// name: Some(String::from("zellij-session")), +// attach: Some(true), +// }; +// +// assert_eq!(expected_session, session_layout); +// } From 9818934aa2fc72c944ad6b02f91e4db2c33969ca Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 17 Aug 2022 09:16:44 +0200 Subject: [PATCH 08/55] work --- zellij-server/src/lib.rs | 6 ++++-- zellij-server/src/os_input_output.rs | 1 - zellij-server/src/screen.rs | 11 +---------- zellij-server/src/tab/mod.rs | 1 + zellij-utils/src/input/layout.rs | 27 +++++++++++++++++++-------- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 0b932579b0..d8fdbc6130 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -324,9 +324,10 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { }; // if !&layout.tabs.is_empty() { - if !&layout.has_tabs() { + if layout.has_tabs() { // for tab_layout in layout.clone().tabs { - for (tab_layout, tab_name) in layout.tabs() { + for (tab_name, tab_layout) in layout.tabs() { + log::info!("tab_name: {:?}, tab_layout: {:?}", tab_name, tab_layout); spawn_tabs(Some(tab_layout.clone()), Some(tab_name)); } @@ -356,6 +357,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { // .unwrap(); // } } else { + log::info!("in else"); spawn_tabs(None, None); } session_data diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs index a2bd840f11..31f1780b8f 100644 --- a/zellij-server/src/os_input_output.rs +++ b/zellij-server/src/os_input_output.rs @@ -374,7 +374,6 @@ impl ServerOsApi for ServerOsInputOutput { Ok(()) } fn send_to_client(&self, client_id: ClientId, msg: ServerToClientMsg) { - log::info!("sending message to client: {:?}", msg); if let Some(sender) = self.client_senders.lock().unwrap().get_mut(&client_id) { sender.send(msg); } diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 4841d4cc58..92016a7f98 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -639,10 +639,9 @@ impl Screen { /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. pub fn new_tab(&mut self, layout: Layout, new_pids: Vec, client_id: ClientId) { - println!("screen.new_tab() 1"); + log::info!("new_tab, layout: {:?}", layout); let tab_index = self.get_new_tab_index(); let position = self.tabs.len(); - println!("screen.new_tab() 2"); let mut tab = Tab::new( tab_index, position, @@ -663,9 +662,7 @@ impl Screen { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), ); - println!("screen.new_tab() 3"); tab.apply_layout(layout, new_pids, tab_index, client_id); - println!("screen.new_tab() 4"); if self.session_is_mirrored { if let Some(active_tab) = self.get_active_tab_mut(client_id) { let client_mode_infos_in_source_tab = active_tab.drain_connected_clients(None); @@ -688,22 +685,16 @@ impl Screen { } self.update_client_tab_focus(client_id, tab_index); } - println!("screen.new_tab() 5"); tab.update_input_modes(); tab.visible(true); - println!("screen.new_tab() 6"); self.tabs.insert(tab_index, tab); - println!("tabs after inserting: {:?}", self.tabs.len()); if !self.active_tab_indices.contains_key(&client_id) { // this means this is a new client and we need to add it to our state properly self.add_client(client_id); } - println!("screen.new_tab() 7: {:?}", self.tabs.len()); self.update_tabs(); - println!("screen.new_tab() 8: {:?}", self.tabs.len()); self.render(); - println!("screen.new_tab() 9: {:?}", self.tabs.len()); } pub fn add_client(&mut self, client_id: ClientId) { diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 44c2a480f2..dbded3de22 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -431,6 +431,7 @@ impl Tab { tab_index: usize, client_id: ClientId, ) { + log::info!("apply layout?"); if self.tiled_panes.has_panes() { log::error!( "Applying a layout to a tab with existing panes - this is not yet supported!" diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 939b700795..411f2c256f 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -846,6 +846,7 @@ impl Layout { } pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(Layout, PaneGeom)> { + log::info!("position_panes_in_space..."); split_space(space, self) } @@ -864,6 +865,8 @@ impl Layout { Ok(()) }, LayoutParts::Tabs(_) => { + // TODO: CONTINUE HERE - if we insert tab and we have tabs, just add it + // here? (and below) Err("Only top layout part can have a tabs block") } } @@ -887,17 +890,26 @@ impl Layout { } pub fn has_tabs(&self) -> bool { - // TODO: CONTINUE HERE (15/08) - implement these, then test with: - // - cargo make build && target/debug/zellij - unimplemented!() + match self.parts { + LayoutParts::Tabs(_) => true, + _ => false + } } - pub fn tabs(&self) -> Vec<(Layout, String)> { // String is the tab name - unimplemented!() + pub fn tabs(&self) -> Vec<(String, Layout)> { // String is the tab name + match &self.parts { + // LayoutParts::Tabs(tabs) => tabs.clone(), + LayoutParts::Tabs(tabs) => { + log::info!("tabs is: {:?}", tabs); + tabs.clone() + } + _ => vec![] + } } pub fn focused_tab_index(&self) -> Option { - unimplemented!() + // TBD + None } // fn from_vec_tab_layout(tab_layout: Vec) -> Result, ConfigError> { @@ -1027,8 +1039,7 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG pane_positions }, LayoutParts::Tabs(tabs) => { - // TODO - unimplemented!() + panic!("tab layout should not have nested tabs"); } } // let mut pane_positions = Vec::new(); From 444c73abc31c1ccedcbbec2a61af1e39f7ff5c80 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 17 Aug 2022 10:55:30 +0200 Subject: [PATCH 09/55] layouts working --- zellij-server/src/lib.rs | 3 +-- zellij-server/src/pty.rs | 10 +++++----- zellij-server/src/tab/mod.rs | 1 - zellij-utils/src/input/layout.rs | 30 +++++++++++++++++++----------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index d8fdbc6130..c1a9778328 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -327,8 +327,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { if layout.has_tabs() { // for tab_layout in layout.clone().tabs { for (tab_name, tab_layout) in layout.tabs() { - log::info!("tab_name: {:?}, tab_layout: {:?}", tab_name, tab_layout); - spawn_tabs(Some(tab_layout.clone()), Some(tab_name)); + spawn_tabs(Some(tab_layout.clone()), tab_name); } if let Some(focused_tab_index) = layout.focused_tab_index() { diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 8d6f898e40..0fc2c7a2b1 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -148,12 +148,12 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { // }); // let merged_layout = layout.template.clone().insert_tab_layout(tab_layout); - let mut layout = layout.clone(); - if let Some(tab_layout) = tab_layout { - layout.insert_tab_layout(&tab_layout).expect("corrupted tab layout"); - } +// let mut layout = layout.clone(); +// if let Some(tab_layout) = tab_layout { +// layout.insert_tab_layout(&tab_layout).expect("corrupted tab layout"); +// } - pty.spawn_terminals_for_layout(*layout, terminal_action.clone(), client_id); + pty.spawn_terminals_for_layout(tab_layout.unwrap_or_else(|| layout.new_tab()), terminal_action.clone(), client_id); if let Some(tab_name) = tab_name { // clear current name at first diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index dbded3de22..44c2a480f2 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -431,7 +431,6 @@ impl Tab { tab_index: usize, client_id: ClientId, ) { - log::info!("apply layout?"); if self.tiled_panes.has_panes() { log::error!( "Applying a layout to a tab with existing panes - this is not yet supported!" diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 411f2c256f..e789c061bc 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -167,11 +167,12 @@ pub struct Layout { pub borderless: bool, pub focus: Option, pub tabs_index_in_children: Option, + template: Option>, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum LayoutParts { - Tabs(Vec<(String, Layout)>), // String is the tab name + Tabs(Vec<(Option, Layout)>), // String is the tab name Panes(Vec), } @@ -679,21 +680,24 @@ impl Layout { borderless, focus: None, // TODO tabs_index_in_children, + template: None, }) } let mut base_layout = parse_kdl_layout(layout_node, direction, &mut tabs)?; if !tabs.is_empty() { let mut root_layout = Layout::default(); - let mut tab_parts: Vec<(String, Layout)> = vec![]; + let mut tab_parts: Vec<(Option, Layout)> = vec![]; for (i, tab) in tabs.drain(..).enumerate() { - let tab_name = format!("{}", i); // TODO: support tab name in layout let mut layout_for_tab = base_layout.clone(); layout_for_tab.insert_tab_layout(&tab); - tab_parts.push((tab_name, layout_for_tab)); + tab_parts.push((None, layout_for_tab)); } root_layout.parts = LayoutParts::Tabs(tab_parts); + let mut layout_template = base_layout.clone(); + layout_template.insert_tab_layout(&Layout::with_one_pane()); + root_layout.template = Some(Box::new(layout_template)); Ok(root_layout) } else { Ok(base_layout) @@ -856,7 +860,15 @@ impl Layout { // self.parts.append(&mut parts); } + pub fn new_tab(&self) -> Layout { + match &self.template { + Some(template) => *template.clone(), + None => self.clone() + } + } pub fn insert_tab_layout(&mut self, tab_layout: &Layout) -> Result<(), &'static str> { + // this inserts a tab layout into a layout that doesn't yet have tabs, + // making sure to place it in the right place inside the existing template match self.tabs_index_in_children { Some(tabs_index_in_children) => { match &mut self.parts { @@ -864,7 +876,7 @@ impl Layout { panes.insert(tabs_index_in_children, tab_layout.clone()); Ok(()) }, - LayoutParts::Tabs(_) => { + LayoutParts::Tabs(tabs) => { // TODO: CONTINUE HERE - if we insert tab and we have tabs, just add it // here? (and below) Err("Only top layout part can have a tabs block") @@ -896,13 +908,9 @@ impl Layout { } } - pub fn tabs(&self) -> Vec<(String, Layout)> { // String is the tab name + pub fn tabs(&self) -> Vec<(Option, Layout)> { // String is the tab name match &self.parts { - // LayoutParts::Tabs(tabs) => tabs.clone(), - LayoutParts::Tabs(tabs) => { - log::info!("tabs is: {:?}", tabs); - tabs.clone() - } + LayoutParts::Tabs(tabs) => tabs.clone(), _ => vec![] } } From 6a4d7cf8ed874b0a21ee82089b07f77cc1179b6f Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 18 Aug 2022 00:55:17 +0200 Subject: [PATCH 10/55] work --- zellij-utils/src/input/layout.rs | 31 ++- zellij-utils/src/input/unit/layout_test.rs | 239 +++++++++++++++++++++ 2 files changed, 259 insertions(+), 11 deletions(-) diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index e789c061bc..707a413279 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -615,11 +615,10 @@ impl Layout { pub fn from_kdl(kdl_layout: &KdlDocument, direction: Option) -> Result { let mut tabs = vec![]; let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; - fn parse_kdl_layout (kdl_layout: &KdlNode, direction: Option, tabs: &mut Vec) -> Result { + fn parse_kdl_layout (kdl_layout: &KdlNode, tabs: &mut Vec) -> Result { let borderless: bool = kdl_get_child_entry_bool_value!(kdl_layout, "borderless").unwrap_or(false); let focus = kdl_get_child_entry_bool_value!(kdl_layout, "focus"); let pane_name = kdl_get_child_entry_string_value!(kdl_layout, "name"); - let direction = direction.unwrap_or_default(); let mut split_size = None; if let Some(string_split_size) = kdl_get_string_entry!(kdl_layout, "size") { // "10%" => SplitSize::Percent(10) or 10 => SplitSize::Fixed(10) @@ -638,18 +637,17 @@ impl Layout { } run = Some(Run::Plugin(RunPlugin::from_kdl(kdl_plugin_block)?)); } + let mut direction = SplitDirection::default(); let mut layout_parts = vec![]; let mut tabs_index_in_children = None; if let Some(kdl_parts) = kdl_get_child!(kdl_layout, "parts") { - let direction = kdl_get_string_entry!(kdl_parts, "direction").ok_or(ConfigError::KdlParsingError("no direction found for layout part".into()))?; - let direction = SplitDirection::from_str(direction)?; - // let mut parts: Vec = vec![]; - // if let Some(children) = kdl_children_nodes!(kdl_layout) { + let part_direction = kdl_get_string_entry!(kdl_parts, "direction").ok_or(ConfigError::KdlParsingError("no direction found for layout part".into()))?; + direction = SplitDirection::from_str(part_direction)?; if let Some(children) = kdl_children_nodes!(kdl_parts) { for (i, child) in children.iter().enumerate() { let child_name = kdl_name!(child); if child_name == "layout" { - layout_parts.push(parse_kdl_layout(&child, Some(direction), tabs)?); + layout_parts.push(parse_kdl_layout(&child, tabs)?); } else if child_name == "tabs" { tabs_index_in_children = Some(i); if !tabs.is_empty() { @@ -658,12 +656,12 @@ impl Layout { match kdl_children_nodes!(child) { Some(children) => { for child in children { - let tab_layout = parse_kdl_layout(&child, Some(direction), tabs)?; + let tab_layout = parse_kdl_layout(&child, tabs)?; tabs.push(tab_layout); } }, - None => tabs.push(Layout::with_one_pane()), + None => tabs.push(Layout::default()), } } else { return Err(ConfigError::KdlParsingError(format!("Unknown layout part: {:?}", child_name))); @@ -683,7 +681,8 @@ impl Layout { template: None, }) } - let mut base_layout = parse_kdl_layout(layout_node, direction, &mut tabs)?; + let mut base_layout = parse_kdl_layout(layout_node, &mut tabs)?; + println!("base_layout: {:?}", base_layout); if !tabs.is_empty() { let mut root_layout = Layout::default(); let mut tab_parts: Vec<(Option, Layout)> = vec![]; @@ -696,9 +695,13 @@ impl Layout { } root_layout.parts = LayoutParts::Tabs(tab_parts); let mut layout_template = base_layout.clone(); - layout_template.insert_tab_layout(&Layout::with_one_pane()); + layout_template.insert_tab_layout(&Layout::default()); root_layout.template = Some(Box::new(layout_template)); Ok(root_layout) + } else if base_layout.is_empty() { + // even a totally empty layout needs at least one pane + base_layout.parts = LayoutParts::Panes(vec![Layout::default()]); + Ok(base_layout) } else { Ok(base_layout) } @@ -901,6 +904,12 @@ impl Layout { } } + pub fn is_empty(&self) -> bool { + match &self.parts { + LayoutParts::Tabs(tabs) => tabs.is_empty(), + LayoutParts::Panes(panes) => panes.is_empty(), + } + } pub fn has_tabs(&self) -> bool { match self.parts { LayoutParts::Tabs(_) => true, diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 07747573d7..b898e9e5d3 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -13,6 +13,245 @@ fn default_layout_dir(layout: String) -> PathBuf { layout_dir.join(layout) } +#[test] +fn empty_layout() { + let kdl_layout = "layout"; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout::with_one_pane(); + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_one_pane() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout; + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout::with_one_pane(); + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_multiple_panes() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout; + layout; + layout; + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default() + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_nested_panes() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + parts direction="Vertical" { + layout; + layout; + } + } + layout { + parts direction="Horizontal" { + layout; + layout; + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + } + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_multiple_nested_panes() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + parts direction="Vertical" { + layout; + layout { + parts direction="Vertical" { + layout; + layout + } + } + } + } + layout; + layout { + parts direction="Horizontal" { + layout; + layout; + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + } + ]), + ..Default::default() + }, + Layout::default(), + Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + } + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_tabs() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + tabs { + layout; + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Tabs(vec![ + (None, Layout::default()), + ]), + ..Default::default() + }; + // TODO: CONTINUE HERE (17/08) + // need to get this test to pass... there's some issue with layouts created with one pane, + // without one pane... need to figure out the best way to go about it + // to test: cargo test -- layout_with_tabs --nocapture + + assert_eq!(layout.parts, expected_layout.parts); +} + +#[test] +fn layout_with_empty_tabs_block() { + // TBD +} + +#[test] +fn layout_with_nested_differing_tabs() { + // TBD +} + +#[test] +fn layout_with_panes_in_different_mixed_split_sizes() { + // TBD +} + +#[test] +fn layout_with_panes_in_different_split_sizes() { + // TBD +} + +#[test] +fn layout_with_command_panes() { + // TBD +} + +#[test] +fn layout_with_plugin_panes() { + // TBD +} + +#[test] +fn layout_with_borderless_panes() { + // TBD +} + +#[test] +fn layout_with_focused_panes() { + // TBD +} + +#[test] +fn layout_with_pane_names() { + // TBD +} + +#[test] +fn layout_with_tab_names() { + // TBD +} + +// TODO: CONTINUE HERE +// - write tests similar to the config that will feed KDL into Layout::from_kdl and assert stuff +// about the layout +// - then bring these tests back // TODO: BRING THESE TESTS BACK!! // // From 3372f24c909515dd65d21c0fd241e1008b6755c7 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 18 Aug 2022 17:23:07 +0200 Subject: [PATCH 11/55] layout tests --- zellij-utils/assets/layouts/default.kdl | 1 - zellij-utils/src/input/command.rs | 2 +- zellij-utils/src/input/layout.rs | 35 +- zellij-utils/src/input/unit/layout_test.rs | 385 ++++++++++++++++++++- 4 files changed, 381 insertions(+), 42 deletions(-) diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index fd07ade1d5..f8dce242a6 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -15,4 +15,3 @@ layout { } } } - diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs index 8321c77fae..1c3a0f6019 100644 --- a/zellij-utils/src/input/command.rs +++ b/zellij-utils/src/input/command.rs @@ -26,7 +26,7 @@ pub struct RunCommand { impl RunCommand { pub fn from_kdl(kdl_node: &KdlNode) -> Result { let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or(ConfigError::KdlParsingError("Command must have a cmd value".into()))?); - let cwd = kdl_get_child_entry_string_value!(kdl_node, "cmd").map(|c| PathBuf::from(c)); + let cwd = kdl_get_child_entry_string_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); let args = match kdl_get_child!(kdl_node, "args") { Some(kdl_args) => { kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect() diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 707a413279..e897c8c35d 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -613,12 +613,12 @@ impl Layout { Layout::from_kdl(&kdl_layout, None) } pub fn from_kdl(kdl_layout: &KdlDocument, direction: Option) -> Result { - let mut tabs = vec![]; + let mut tabs: Vec<(Option, Layout)> = vec![]; let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; - fn parse_kdl_layout (kdl_layout: &KdlNode, tabs: &mut Vec) -> Result { + fn parse_kdl_layout (kdl_layout: &KdlNode, tabs: &mut Vec<(Option, Layout)>) -> Result { let borderless: bool = kdl_get_child_entry_bool_value!(kdl_layout, "borderless").unwrap_or(false); let focus = kdl_get_child_entry_bool_value!(kdl_layout, "focus"); - let pane_name = kdl_get_child_entry_string_value!(kdl_layout, "name"); + let pane_name = kdl_get_child_entry_string_value!(kdl_layout, "name").map(|name| name.to_string()); let mut split_size = None; if let Some(string_split_size) = kdl_get_string_entry!(kdl_layout, "size") { // "10%" => SplitSize::Percent(10) or 10 => SplitSize::Fixed(10) @@ -656,12 +656,13 @@ impl Layout { match kdl_children_nodes!(child) { Some(children) => { for child in children { + let tab_name = kdl_get_string_entry!(child, "tab_name").map(|tab_name| tab_name.to_string()); let tab_layout = parse_kdl_layout(&child, tabs)?; - tabs.push(tab_layout); + tabs.push((tab_name, tab_layout)); } }, - None => tabs.push(Layout::default()), + None => tabs.push((None, Layout::default())), } } else { return Err(ConfigError::KdlParsingError(format!("Unknown layout part: {:?}", child_name))); @@ -671,31 +672,32 @@ impl Layout { } Ok(Layout { direction, - pane_name: None, // TODO + pane_name, parts: LayoutParts::Panes(layout_parts), split_size, run, borderless, - focus: None, // TODO + focus, tabs_index_in_children, template: None, }) } let mut base_layout = parse_kdl_layout(layout_node, &mut tabs)?; - println!("base_layout: {:?}", base_layout); if !tabs.is_empty() { let mut root_layout = Layout::default(); let mut tab_parts: Vec<(Option, Layout)> = vec![]; - for (i, tab) in tabs.drain(..).enumerate() { + for (i, (tab_name, tab)) in tabs.drain(..).enumerate() { let mut layout_for_tab = base_layout.clone(); layout_for_tab.insert_tab_layout(&tab); - tab_parts.push((None, layout_for_tab)); + layout_for_tab.tabs_index_in_children = None; + tab_parts.push((tab_name, layout_for_tab)); } root_layout.parts = LayoutParts::Tabs(tab_parts); let mut layout_template = base_layout.clone(); layout_template.insert_tab_layout(&Layout::default()); + layout_template.tabs_index_in_children = None; root_layout.template = Some(Box::new(layout_template)); Ok(root_layout) } else if base_layout.is_empty() { @@ -1146,16 +1148,7 @@ impl TryFrom for RunPluginLocation { "zellij" => Ok(Self::Zellij(PluginTag::new(url.path()))), "file" => { let path = PathBuf::from(url.path()); - let canonicalize = |p: &Path| { - fs::canonicalize(p) - .map_err(|_| PluginsConfigError::InvalidPluginLocation(p.to_owned())) - }; - canonicalize(&path) - .or_else(|_| match path.strip_prefix("/") { - Ok(path) => canonicalize(path), - Err(_) => Err(PluginsConfigError::InvalidPluginLocation(path.to_owned())), - }) - .map(Self::File) + Ok(Self::File(path)) }, _ => Err(PluginsConfigError::InvalidUrl(url)), } @@ -1320,7 +1313,7 @@ impl FromStr for SplitSize { fn from_str(s: &str) -> Result { if s.chars().last() == Some('%') { let char_count = s.chars().count(); - let percent_size = usize::from_str_radix(&s[..char_count], 10)?; + let percent_size = usize::from_str_radix(&s[..char_count.saturating_sub(1)], 10)?; Ok(SplitSize::Percent(percent_size)) } else { let fixed_size = usize::from_str_radix(s, 10)?; diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index b898e9e5d3..0162c41833 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -186,66 +186,413 @@ fn layout_with_tabs() { let expected_layout = Layout { direction: SplitDirection::Horizontal, parts: LayoutParts::Tabs(vec![ - (None, Layout::default()), + (None, Layout::with_one_pane()), ]), + template: Some(Box::new(Layout::with_one_pane())), ..Default::default() }; - // TODO: CONTINUE HERE (17/08) - // need to get this test to pass... there's some issue with layouts created with one pane, - // without one pane... need to figure out the best way to go about it - // to test: cargo test -- layout_with_tabs --nocapture - - assert_eq!(layout.parts, expected_layout.parts); + assert_eq!(layout, expected_layout); } #[test] fn layout_with_empty_tabs_block() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + tabs; + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Tabs(vec![ + (None, Layout::with_one_pane()), + ]), + template: Some(Box::new(Layout::with_one_pane())), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_nested_differing_tabs() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout; + tabs { + layout { + parts direction="Vertical" { + layout; + layout; + layout; + } + } + layout { + parts direction="Horizontal" { + layout; + layout; + layout; + } + } + } + layout; + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Tabs(vec![ + (None, Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![Layout::default(), Layout::default(), Layout::default()]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }), + (None, Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![Layout::default(), Layout::default(), Layout::default()]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }), + ]), + template: Some(Box::new(Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default(), + ]), + ..Default::default() + })), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_panes_in_different_mixed_split_sizes() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout size=1; + layout size="10%"; + layout; + layout size=2; + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + split_size: Some(SplitSize::Fixed(1)), + ..Default::default() + }, + Layout { + split_size: Some(SplitSize::Percent(10)), + ..Default::default() + }, + Layout { + split_size: None, + ..Default::default() + }, + Layout { + split_size: Some(SplitSize::Fixed(2)), + ..Default::default() + }, + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] -fn layout_with_panes_in_different_split_sizes() { - // TBD +fn layout_with_command_panes() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + command { + cmd "htop" + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + ..Default::default() + })), + ..Default::default() + }, + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] -fn layout_with_command_panes() { - // TBD +fn layout_with_command_panes_and_cwd() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + command { + cmd "htop" + cwd "/path/to/my/cwd" + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + ..Default::default() + })), + ..Default::default() + }, + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_command_panes_and_cwd_and_args() { + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + command { + cmd "htop" + args "-h" "-v" + cwd "/path/to/my/cwd" + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + args: vec![String::from("-h"), String::from("-v")], + cwd: Some(PathBuf::from("/path/to/my/cwd")), + })), + ..Default::default() + }, + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_plugin_panes() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + plugin { + location "zellij:tab-bar" + } + } + layout { + plugin { + location "file:/path/to/my/plugin.wasm" + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Plugin(RunPlugin { + location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + _allow_exec_host_cmd: false, + })), + ..Default::default() + }, + Layout { + run: Some(Run::Plugin(RunPlugin { + location: RunPluginLocation::File(PathBuf::from("/path/to/my/plugin.wasm")), + _allow_exec_host_cmd: false, + })), + ..Default::default() + }, + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_borderless_panes() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + borderless true + } + layout + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + parts: LayoutParts::Panes(vec![ + Layout { + borderless: true, + ..Default::default() + }, + Layout::default() + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_focused_panes() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + focus true + } + layout + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + parts: LayoutParts::Panes(vec![ + Layout { + focus: Some(true), + ..Default::default() + }, + Layout::default() + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_pane_names() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + layout { + name "my awesome pane" + } + layout + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + parts: LayoutParts::Panes(vec![ + Layout { + pane_name: Some("my awesome pane".into()), + ..Default::default() + }, + Layout::default() + ]), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } #[test] fn layout_with_tab_names() { - // TBD + let kdl_layout = r#" + layout { + parts direction="Horizontal" { + tabs { + layout tab_name="my cool tab name 1" { + name "pane name inside tab 1" + } + layout tab_name="my cool tab name 2" { + name "pane name inside tab 2" + } + } + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Tabs(vec![ + (Some("my cool tab name 1".into()), Layout { + parts: LayoutParts::Panes(vec![ + Layout { + pane_name: Some("pane name inside tab 1".into()), + ..Default::default() + } + ]), + ..Default::default() + }), + (Some("my cool tab name 2".into()), Layout { + parts: LayoutParts::Panes(vec![ + Layout { + pane_name: Some("pane name inside tab 2".into()), + ..Default::default() + } + ]), + ..Default::default() + }), + ]), + template: Some(Box::new(Layout::with_one_pane())), + ..Default::default() + }; + assert_eq!(layout, expected_layout); } // TODO: CONTINUE HERE From cd4bb8df82ebad70cf171a7b1b96bb598faa1267 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 19 Aug 2022 10:29:02 +0200 Subject: [PATCH 12/55] work --- zellij-utils/src/input/unit/layout_test.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 0162c41833..e6e904bfaa 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -595,9 +595,18 @@ fn layout_with_tab_names() { assert_eq!(layout, expected_layout); } -// TODO: CONTINUE HERE +// TODO: +// - session name +// - focus for pane/tab +// - other stuff in layouts +// - merge layouts with config +// - default layout files +// - open new tab with layout template (maybe in tab_integration_tests?) +// - empty layout + +// TODO: CONTINUE HERE (18/08) // - write tests similar to the config that will feed KDL into Layout::from_kdl and assert stuff -// about the layout +// about the layout - DONE // - then bring these tests back // TODO: BRING THESE TESTS BACK!! // From f4f3a37fbb24b8599bd6b4a6893281225a259c6e Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sat, 20 Aug 2022 17:46:40 +0200 Subject: [PATCH 13/55] work --- src/commands.rs | 72 ++++++++------- zellij-server/src/lib.rs | 3 + zellij-server/src/pty.rs | 1 + .../assets/config/test_config_deleteme.kdl | 18 ++-- zellij-utils/assets/layouts/default.kdl | 87 ++++++++++++++++--- zellij-utils/src/input/config.rs | 45 +++++++++- zellij-utils/src/input/layout.rs | 26 ++++-- zellij-utils/src/input/options.rs | 25 ++++++ zellij-utils/src/input/unit/layout_test.rs | 3 +- zellij-utils/src/kdl.rs | 16 +++- zellij-utils/src/setup.rs | 3 - 11 files changed, 229 insertions(+), 70 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 9c30cc3db9..5f9935e8ea 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -318,43 +318,41 @@ pub(crate) fn start_client(opts: CliArgs) { ); } else { // TODO: bring this back -// if let Some(layout_some) = layout.clone() { -// if let Some(session_name) = layout_some.session.name { -// if layout_some.session.attach.unwrap() { -// let client = attach_with_session_name( -// Some(session_name), -// config_options.clone(), -// true, -// ); -// -// let attach_layout = match client { -// ClientInfo::Attach(_, _) => None, -// ClientInfo::New(_) => layout, -// }; -// -// start_client_impl( -// Box::new(os_input), -// opts, -// config, -// config_options, -// client, -// attach_layout, -// ); -// } else { -// start_client_plan(session_name.clone()); -// start_client_impl( -// Box::new(os_input), -// opts, -// config, -// config_options, -// ClientInfo::New(session_name), -// layout, -// ); -// } -// -// process::exit(0); -// } -// } + if let Some(session_name) = config_options.session_name.as_ref() { + match config_options.attach_to_session { + Some(true) => { + let client = attach_with_session_name( + Some(session_name.clone()), + config_options.clone(), + true, + ); + let attach_layout = match client { + ClientInfo::Attach(_, _) => None, + ClientInfo::New(_) => Some(layout), + }; + start_client_impl( + Box::new(os_input), + opts, + config, + config_options, + client, + attach_layout, + ); + }, + _ => { + start_client_plan(session_name.clone()); + start_client_impl( + Box::new(os_input), + opts, + config, + config_options.clone(), + ClientInfo::New(session_name.clone()), + Some(layout), + ); + } + } + process::exit(0); // TODO: why is this here? + } let session_name = names::Generator::default().next().unwrap(); start_client_plan(session_name.clone()); diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index c1a9778328..85b36df3bb 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -325,12 +325,15 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { // if !&layout.tabs.is_empty() { if layout.has_tabs() { + log::info!("layout.has_tabs()"); // for tab_layout in layout.clone().tabs { for (tab_name, tab_layout) in layout.tabs() { spawn_tabs(Some(tab_layout.clone()), tab_name); } + log::info!("done spawned tab"); if let Some(focused_tab_index) = layout.focused_tab_index() { + log::info!("focused_tab_index: {:?}", focused_tab_index); session_data .read() .unwrap() diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 0fc2c7a2b1..7f56e30524 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -133,6 +133,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { pty.set_active_pane(pane_id, client_id); }, PtyInstruction::GoToTab(tab_index, client_id) => { + log::info!("GoToTab {:?}", tab_index); pty.bus .senders .send_to_screen(ScreenInstruction::GoToTab(tab_index, Some(client_id))) diff --git a/zellij-utils/assets/config/test_config_deleteme.kdl b/zellij-utils/assets/config/test_config_deleteme.kdl index 25d8403750..658cd1d41e 100644 --- a/zellij-utils/assets/config/test_config_deleteme.kdl +++ b/zellij-utils/assets/config/test_config_deleteme.kdl @@ -6,7 +6,7 @@ keybinds { bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl s" { SwitchToMode "Search"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } @@ -34,7 +34,7 @@ keybinds { bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl s" { SwitchToMode "Search"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } @@ -58,7 +58,7 @@ keybinds { bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl s" { SwitchToMode "Search"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } @@ -91,7 +91,7 @@ keybinds { bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl s" { SwitchToMode "Search"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl q" { Quit; } bind "n" "Tab" { MovePane; } @@ -112,7 +112,7 @@ keybinds { bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl s" { SwitchToMode "Search"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } bind "Ctrl o" { SwitchToMode "Session"; } @@ -141,7 +141,7 @@ keybinds { bind "Alt +" "Alt =" { Resize "Increase"; } bind "Alt -" { Resize "Decrease"; } } - scroll { + search { bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl g" { SwitchToMode "Locked"; } @@ -200,7 +200,7 @@ keybinds { bind "Ctrl b" { SwitchToMode "Tmux"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } + bind "Ctrl s" { SwitchToMode "Search"; } bind "Ctrl q" { Quit; } bind "d" { Detach; } bind "Alt n" { NewPane; } @@ -218,7 +218,7 @@ keybinds { bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "[" { SwitchToMode "Scroll"; } + bind "[" { SwitchToMode "Search"; } bind "Ctrl q" { Quit; } bind "Ctrl b" { Write 2; SwitchToMode "Normal"; } bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } @@ -248,6 +248,8 @@ keybinds { bind "d" { Detach; } } } +// session_name "my-foo-session" +// attach_to_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 diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index f8dce242a6..6d8ce9c93b 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -1,17 +1,84 @@ +// layout { +// parts direction="Horizontal" { +// layout size=1 { +// borderless true +// plugin { +// location "zellij:tab-bar" +// } +// } +// tabs; +// layout size=2 { +// borderless true +// plugin { +// location "zellij:status-bar" +// } +// } +// } +// } + +// TODO: CONTINUE HERE (20/08) - consider implementing this +// with tab templates layout { - parts direction="Horizontal" { - layout size=1 { - borderless true - plugin { - location "zellij:tab-bar" + tab_template name="plugins" { + pane size=1 borderless=true { + plugin location="zellij:tab-bar"; + } + tab_body + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + tabs focused_index=1 { + tab template="plugins" name="some tab name" { + body split_direction="Vertical" { + pane + pane + pane } } - tabs; - layout size=2 { - borderless true - plugin { - location "zellij:status-bar" + tab template="plugins" + tab + } +} + +// without templates +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar"; + } + pane + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} + +// with pane templates +layout { + tab_template name="plugins" { + pane size=1 borderless=true { + plugin location="zellij:tab-bar"; + } + tab_body + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + pane_template name="3 vertical panes" { + pane_body split_direction="Vertical" { + pane + pane + pane + } + } + tabs focused_index=1 { + tab template="plugins" name="some tab name" { + tab_body split_direction="Horizontal" { + pane template="3 vertical panes" + pane + pane template="3 vertical panes" } } + tab template="plugins" + tab } } diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 8ed79e1e2f..dad9478816 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -676,12 +676,53 @@ mod config_test { #[test] fn can_define_options_in_configfile() { // TODO: consider writing a macro to generate a test like this for each option + + + + + + + + let config_contents = r#" simplified_ui true + theme "my cool theme" + default_mode "locked" + default_shell "/path/to/my/shell" + default_layout "/path/to/my/layout.kdl" + layout_dir "/path/to/my/layout-dir" + theme_dir "/path/to/my/theme-dir" + mouse_mode false + pane_frames false + mirror_session true + on_force_close "quit" + scroll_buffer_size 100000 + copy_command "/path/to/my/copy-command" + copy_clipboard "primary" + copy_on_select false + scrollback_editor "/path/to/my/scrollback-editor" + session_name "my awesome session" + attach_to_session true "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let simplified_ui_in_config = config.options.simplified_ui; - assert_eq!(simplified_ui_in_config, Some(true), "Option set in config"); + assert_eq!(config.options.simplified_ui, Some(true), "Option set in config"); + assert_eq!(config.options.theme, Some(String::from("my cool theme")), "Option set in config"); + assert_eq!(config.options.default_mode, Some(InputMode::Locked), "Option set in config"); + assert_eq!(config.options.default_shell, Some(PathBuf::from("/path/to/my/shell")), "Option set in config"); + assert_eq!(config.options.default_layout, Some(PathBuf::from("/path/to/my/layout.kdl")), "Option set in config"); + assert_eq!(config.options.layout_dir, Some(PathBuf::from("/path/to/my/layout-dir")), "Option set in config"); + assert_eq!(config.options.theme_dir, Some(PathBuf::from("/path/to/my/theme-dir")), "Option set in config"); + assert_eq!(config.options.mouse_mode, Some(false), "Option set in config"); + assert_eq!(config.options.pane_frames, Some(false), "Option set in config"); + assert_eq!(config.options.mirror_session, Some(true), "Option set in config"); + assert_eq!(config.options.on_force_close, Some(OnForceClose::Quit), "Option set in config"); + assert_eq!(config.options.scroll_buffer_size, Some(100000), "Option set in config"); + assert_eq!(config.options.copy_command, Some(String::from("/path/to/my/copy-command")), "Option set in config"); + assert_eq!(config.options.copy_clipboard, Some(Clipboard::Primary), "Option set in config"); + assert_eq!(config.options.copy_on_select, Some(false), "Option set in config"); + assert_eq!(config.options.scrollback_editor, Some(PathBuf::from("/path/to/my/scrollback-editor")), "Option set in config"); + assert_eq!(config.options.session_name, Some(String::from("my awesome session")), "Option set in config"); + assert_eq!(config.options.attach_to_session, Some(true), "Option set in config"); } #[test] diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index e897c8c35d..3005fffe83 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -166,7 +166,11 @@ pub struct Layout { #[serde(default)] pub borderless: bool, pub focus: Option, + // TODO: move these elsewhere? + pub session_name: Option, + pub attach_to_session: bool, pub tabs_index_in_children: Option, + focused_tab_index: Option, template: Option>, } @@ -615,7 +619,8 @@ impl Layout { pub fn from_kdl(kdl_layout: &KdlDocument, direction: Option) -> Result { let mut tabs: Vec<(Option, Layout)> = vec![]; let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; - fn parse_kdl_layout (kdl_layout: &KdlNode, tabs: &mut Vec<(Option, Layout)>) -> Result { + let mut focused_tab_index = None; + fn parse_kdl_layout (kdl_layout: &KdlNode, tabs: &mut Vec<(Option, Layout)>, focused_tab_index: &mut Option) -> Result { let borderless: bool = kdl_get_child_entry_bool_value!(kdl_layout, "borderless").unwrap_or(false); let focus = kdl_get_child_entry_bool_value!(kdl_layout, "focus"); let pane_name = kdl_get_child_entry_string_value!(kdl_layout, "name").map(|name| name.to_string()); @@ -647,17 +652,22 @@ impl Layout { for (i, child) in children.iter().enumerate() { let child_name = kdl_name!(child); if child_name == "layout" { - layout_parts.push(parse_kdl_layout(&child, tabs)?); + layout_parts.push(parse_kdl_layout(&child, tabs, focused_tab_index)?); } else if child_name == "tabs" { tabs_index_in_children = Some(i); if !tabs.is_empty() { return Err(ConfigError::KdlParsingError(format!("Only one 'tabs' section allowed per layout..."))); } + log::info!("child: {:?}", child); + if let Some(idx) = kdl_get_int_entry!(child, "focused_tab_index") { + log::info!("got idx: {:?}", idx); + let _ = focused_tab_index.insert(idx as usize); + } match kdl_children_nodes!(child) { Some(children) => { for child in children { let tab_name = kdl_get_string_entry!(child, "tab_name").map(|tab_name| tab_name.to_string()); - let tab_layout = parse_kdl_layout(&child, tabs)?; + let tab_layout = parse_kdl_layout(&child, tabs, focused_tab_index)?; tabs.push((tab_name, tab_layout)); } @@ -680,11 +690,15 @@ impl Layout { focus, tabs_index_in_children, template: None, + session_name: None, + attach_to_session: false, + focused_tab_index: None, }) } - let mut base_layout = parse_kdl_layout(layout_node, &mut tabs)?; + let mut base_layout = parse_kdl_layout(layout_node, &mut tabs, &mut focused_tab_index)?; if !tabs.is_empty() { let mut root_layout = Layout::default(); + root_layout.focused_tab_index = focused_tab_index; let mut tab_parts: Vec<(Option, Layout)> = vec![]; for (i, (tab_name, tab)) in tabs.drain(..).enumerate() { @@ -927,8 +941,8 @@ impl Layout { } pub fn focused_tab_index(&self) -> Option { - // TBD - None + log::info!("focused_tab_index? {:?}", self.focused_tab_index); + self.focused_tab_index } // fn from_vec_tab_layout(tab_layout: Vec) -> Result, ConfigError> { diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 650d75f8b2..870791ae30 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -107,6 +107,14 @@ pub struct Options { /// Explicit full path to open the scrollback editor (default is $EDITOR or $VISUAL) #[clap(long, value_parser)] pub scrollback_editor: Option, + + #[clap(long, value_parser)] + #[serde(default)] + pub session_name: Option, + + #[clap(long, value_parser)] + #[serde(default)] + pub attach_to_session: Option, } #[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] @@ -231,6 +239,12 @@ impl Options { let scrollback_editor = other .scrollback_editor .or_else(|| self.scrollback_editor.clone()); + let session_name = other + .session_name + .or_else(|| self.session_name.clone()); + let attach_to_session = other + .attach_to_session + .or_else(|| self.attach_to_session.clone()); Options { simplified_ui, @@ -249,6 +263,8 @@ impl Options { copy_clipboard, copy_on_select, scrollback_editor, + session_name, + attach_to_session, } } @@ -286,6 +302,12 @@ impl Options { let scrollback_editor = other .scrollback_editor .or_else(|| self.scrollback_editor.clone()); + let session_name = other + .session_name + .or_else(|| self.session_name.clone()); + let attach_to_session = other + .attach_to_session + .or_else(|| self.attach_to_session.clone()); Options { simplified_ui, @@ -304,6 +326,8 @@ impl Options { copy_clipboard, copy_on_select, scrollback_editor, + session_name, + attach_to_session, } } @@ -358,6 +382,7 @@ impl From for Options { copy_clipboard: opts.copy_clipboard, copy_on_select: opts.copy_on_select, scrollback_editor: opts.scrollback_editor, + ..Default::default() } } } diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index e6e904bfaa..b75fdb05e7 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -596,7 +596,8 @@ fn layout_with_tab_names() { } // TODO: -// - session name +// - session name - added to the config, TODO (CONTINUE HERE 19/08): add test for it and for +// attach_to_session in the config - DONE // - focus for pane/tab // - other stuff in layouts // - merge layouts with config diff --git a/zellij-utils/src/kdl.rs b/zellij-utils/src/kdl.rs index 99a073133e..76ef94f118 100644 --- a/zellij-utils/src/kdl.rs +++ b/zellij-utils/src/kdl.rs @@ -631,6 +631,12 @@ impl Options { .map(|theme| theme.to_string()); let default_mode = kdl_property_first_arg_as_string!(kdl_options, "default_mode") .and_then(|default_mode| InputMode::try_from(default_mode).ok()); + let default_layout = kdl_property_first_arg_as_string!(kdl_options, "default_layout") + .map(|default_layout| PathBuf::from(default_layout)); + let layout_dir = kdl_property_first_arg_as_string!(kdl_options, "layout_dir") + .map(|layout_dir| PathBuf::from(layout_dir)); + let theme_dir = kdl_property_first_arg_as_string!(kdl_options, "theme_dir") + .map(|theme_dir| PathBuf::from(theme_dir)); let mouse_mode = kdl_property_first_arg_as_bool!(kdl_options, "mouse_mode"); let scroll_buffer_size = kdl_property_first_arg_as_i64!(kdl_options, "scroll_buffer_size") .map(|scroll_buffer_size| scroll_buffer_size as usize); @@ -642,14 +648,16 @@ impl Options { let scrollback_editor = kdl_property_first_arg_as_string!(kdl_options, "scrollback_editor") .map(|scrollback_editor| PathBuf::from(scrollback_editor)); let mirror_session = kdl_property_first_arg_as_bool!(kdl_options, "mirror_session"); + let session_name = kdl_property_first_arg_as_string!(kdl_options, "session_name").map(|s| s.into()); + let attach_to_session = kdl_property_first_arg_as_bool!(kdl_options, "attach_to_session"); Options { simplified_ui, theme, default_mode, default_shell, - default_layout: Some(PathBuf::from("default")), // TODO - layout_dir: None, // TODO - theme_dir: None, // TODO + default_layout, + layout_dir, + theme_dir, mouse_mode, pane_frames, mirror_session, @@ -659,6 +667,8 @@ impl Options { copy_clipboard, copy_on_select, scrollback_editor, + session_name, + attach_to_session, } } } diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index 38035f5aeb..aa5fd364ec 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -247,9 +247,7 @@ impl Setup { // return Err(e); // }, // }; - log::info!("building layout"); let layout = Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir)?; - log::info!("built layout: {:?}", layout); if let Some(Command::Setup(ref setup)) = &opts.command { setup @@ -263,7 +261,6 @@ impl Setup { ); }; - // unimplemented!() Ok((config, layout, config_options)) // TODO: no!!!!!111oneoneone // Setup::merge_config_with_layout(config, layout, config_options) } From 838f5a720ebc68bd05e789e4617dc2001728c941 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Tue, 30 Aug 2022 09:52:36 +0200 Subject: [PATCH 14/55] feat(parsing): kdl layouts without config --- Cargo.lock | 1 + zellij-server/src/panes/tiled_panes/mod.rs | 19 +- .../src/panes/tiled_panes/pane_resizer.rs | 8 +- zellij-server/src/pty.rs | 1 - zellij-server/src/screen.rs | 1 - zellij-server/src/tab/mod.rs | 1 - .../src/tab/unit/tab_integration_tests.rs | 3 +- zellij-utils/Cargo.toml | 2 + zellij-utils/assets/layouts/default.kdl | 77 +- zellij-utils/src/input/layout.rs | 876 +---- zellij-utils/src/input/unit/layout_test.rs | 2843 ++++++++++------- ...n_not_as_first_child_of_pane_template.snap | 222 ++ ...en_not_as_first_child_of_tab_template.snap | 247 ++ ..._and_pane_template_both_with_children.snap | 331 ++ ...t_with_nested_branched_pane_templates.snap | 202 ++ ...st__layout_with_nested_pane_templates.snap | 153 + ...t__layout_with_tab_and_pane_templates.snap | 166 + zellij-utils/src/kdl/kdl_layout_parser.rs | 528 +++ zellij-utils/src/{kdl.rs => kdl/mod.rs} | 41 +- zellij-utils/src/pane_size.rs | 11 + 20 files changed, 3641 insertions(+), 2092 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap create mode 100644 zellij-utils/src/kdl/kdl_layout_parser.rs rename zellij-utils/src/{kdl.rs => kdl/mod.rs} (96%) diff --git a/Cargo.lock b/Cargo.lock index eaf2337c75..663c8578f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3466,6 +3466,7 @@ dependencies = [ "colorsys", "crossbeam", "directories-next", + "insta", "interprocess", "kdl", "knuffel", diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 0358fc762a..d684912b51 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -455,14 +455,17 @@ impl TiledPanes { *display_area, *viewport, ); - if pane_grid.layout(SplitDirection::Horizontal, cols).is_ok() { - let column_difference = cols as isize - display_area.cols as isize; - // FIXME: Should the viewport be an Offset? - viewport.cols = (viewport.cols as isize + column_difference) as usize; - display_area.cols = cols; - } else { - log::error!("Failed to horizontally resize the tab!!!"); - } + match pane_grid.layout(SplitDirection::Horizontal, cols) { + Ok(_) => { + let column_difference = cols as isize - display_area.cols as isize; + // FIXME: Should the viewport be an Offset? + viewport.cols = (viewport.cols as isize + column_difference) as usize; + display_area.cols = cols; + }, + Err(e) => { + log::error!("Failed to horizontally resize the tab: {:?}", e); + } + }; if pane_grid.layout(SplitDirection::Vertical, rows).is_ok() { let row_difference = rows as isize - display_area.rows as isize; viewport.rows = (viewport.rows as isize + row_difference) as usize; diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index 8667e2f6f8..6ed8ddd818 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -178,9 +178,11 @@ impl<'a> PaneResizer<'a> { .filter(|p| { let s = self.get_span(!direction, p.as_ref()); let span_bounds = (s.pos, s.pos + s.size.as_usize()); - bwn(span_bounds.0, boundary) - || (bwn(boundary.0, span_bounds) - && (bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1)) + bwn(span_bounds.0, boundary) || ( + bwn(boundary.0, span_bounds) && ( + bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1 + ) + ) }) .map(|p| self.get_span(direction, p.as_ref())) .collect(); diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 7f56e30524..0fc2c7a2b1 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -133,7 +133,6 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { pty.set_active_pane(pane_id, client_id); }, PtyInstruction::GoToTab(tab_index, client_id) => { - log::info!("GoToTab {:?}", tab_index); pty.bus .senders .send_to_screen(ScreenInstruction::GoToTab(tab_index, Some(client_id))) diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index b8e2cd7e9c..deba6eb9c8 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -649,7 +649,6 @@ impl Screen { /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. pub fn new_tab(&mut self, layout: Layout, new_pids: Vec, client_id: ClientId) { - log::info!("new_tab, layout: {:?}", layout); let tab_index = self.get_new_tab_index(); let position = self.tabs.len(); let mut tab = Tab::new( diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 750f494479..f7f64cff92 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -2048,7 +2048,6 @@ impl Tab { ); if let Some(mouse_event) = active_pane.mouse_middle_click(&relative_position, true) { - log::info!("can have mouse event: {:?}", mouse_event); self.write_to_active_terminal(mouse_event.into_bytes(), client_id); return true; // we need to re-render in this case so the selection disappears } diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index c89d3eda13..35337b44c0 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -201,7 +201,8 @@ fn create_new_tab_with_mock_pty_writer( terminal_emulator_color_codes, ); tab.apply_layout( - LayoutTemplate::default().try_into().unwrap(), + // LayoutTemplate::default().try_into().unwrap(), + Layout::default(), vec![1], index, client_id, diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 46adcdda56..9e59c01607 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -48,6 +48,8 @@ interprocess = "1.1.1" async-std = { version = "1.3.0", features = ["unstable"] } [dev-dependencies] +insta = { version = "1.6.0", features = ["backtrace"] } + [features] disable_automatic_asset_installation = [] diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index 6d8ce9c93b..4434b29b1f 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -1,84 +1,9 @@ -// layout { -// parts direction="Horizontal" { -// layout size=1 { -// borderless true -// plugin { -// location "zellij:tab-bar" -// } -// } -// tabs; -// layout size=2 { -// borderless true -// plugin { -// location "zellij:status-bar" -// } -// } -// } -// } - -// TODO: CONTINUE HERE (20/08) - consider implementing this -// with tab templates -layout { - tab_template name="plugins" { - pane size=1 borderless=true { - plugin location="zellij:tab-bar"; - } - tab_body - pane size=2 borderless=true { - plugin location="zellij:status-bar" - } - } - tabs focused_index=1 { - tab template="plugins" name="some tab name" { - body split_direction="Vertical" { - pane - pane - pane - } - } - tab template="plugins" - tab - } -} - -// without templates layout { pane size=1 borderless=true { - plugin location="zellij:tab-bar"; + plugin location="zellij:tab-bar" } pane pane size=2 borderless=true { plugin location="zellij:status-bar" } } - -// with pane templates -layout { - tab_template name="plugins" { - pane size=1 borderless=true { - plugin location="zellij:tab-bar"; - } - tab_body - pane size=2 borderless=true { - plugin location="zellij:status-bar" - } - } - pane_template name="3 vertical panes" { - pane_body split_direction="Vertical" { - pane - pane - pane - } - } - tabs focused_index=1 { - tab template="plugins" name="some tab name" { - tab_body split_direction="Horizontal" { - pane template="3 vertical panes" - pane - pane template="3 vertical panes" - } - } - tab template="plugins" - tab - } -} diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 3005fffe83..1ab58f550a 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -17,13 +17,15 @@ use crate::{ setup, }; +use crate::kdl::kdl_layout_parser::KdlLayoutParser; use kdl::*; use std::str::FromStr; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use crate::{ kdl_children, + kdl_string_arguments, kdl_children_nodes, kdl_name, kdl_document_name, @@ -32,6 +34,9 @@ use crate::{ kdl_get_child_entry_bool_value, kdl_get_child_entry_string_value, kdl_get_child, + kdl_get_bool_property_or_child_value, + kdl_get_string_property_or_child_value, + kdl_get_int_property_or_child_value, }; use super::{ @@ -83,23 +88,6 @@ pub enum Run { Command(RunCommand), } -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -// #[serde(crate = "self::serde")] -// pub enum RunFromYaml { -// #[serde(rename = "plugin")] -// Plugin(RunPluginFromYaml), -// #[serde(rename = "command")] -// Command(RunCommand), -// } - -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -// #[serde(crate = "self::serde")] -// pub struct RunPluginFromYaml { -// #[serde(default)] -// pub _allow_exec_host_cmd: bool, -// pub location: Url, -// } - #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct RunPlugin { #[serde(default)] @@ -169,9 +157,9 @@ pub struct Layout { // TODO: move these elsewhere? pub session_name: Option, pub attach_to_session: bool, - pub tabs_index_in_children: Option, - focused_tab_index: Option, - template: Option>, + pub external_children_index: Option, + pub focused_tab_index: Option, + pub template: Option>, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -191,6 +179,17 @@ impl LayoutParts { } } } + pub fn insert_pane(&mut self, index: usize, layout: Layout) -> Result<(), ConfigError> { + match self { + LayoutParts::Panes(panes) => { + panes.insert(index, layout); + Ok(()) + }, + LayoutParts::Tabs(_tabs) => { + Err(ConfigError::KdlParsingError("Trying to insert a pane into a tab layout".into())) + } + } + } } impl Default for LayoutParts { @@ -199,369 +198,6 @@ impl Default for LayoutParts { } } -// The struct that is used to deserialize the layout from -// a yaml configuration file, is needed because of: -// https://github.com/bincode-org/bincode/issues/245 -// flattened fields don't retain size information. -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -// #[serde(crate = "self::serde")] -// #[serde(default)] -// pub struct LayoutFromYamlIntermediate { -// #[serde(default)] -// pub template: LayoutTemplate, -// #[serde(default)] -// pub borderless: bool, -// #[serde(default)] -// pub tabs: Vec, -// #[serde(default)] -// pub session: SessionFromYaml, -// #[serde(flatten)] -// pub config: Option, -// } - -// // The struct that is used to deserialize the layout from -// // a yaml configuration file -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] -// #[serde(crate = "self::serde")] -// #[serde(default)] -// pub struct LayoutFromYaml { -// #[serde(default)] -// pub session: SessionFromYaml, -// #[serde(default)] -// pub template: LayoutTemplate, -// #[serde(default)] -// pub borderless: bool, -// #[serde(default)] -// pub tabs: Vec, -// } - -// type LayoutFromYamlIntermediateResult = Result; - -// impl LayoutFromYamlIntermediate { -// pub fn from_path(layout_path: &Path) -> LayoutFromYamlIntermediateResult { -// let mut layout_file = File::open(&layout_path) -// .or_else(|_| File::open(&layout_path.with_extension("yaml"))) -// .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; -// -// let mut layout = String::new(); -// layout_file.read_to_string(&mut layout)?; -// let layout: Option = match serde_yaml::from_str(&layout) { -// Err(e) => { -// // needs direct check, as `[ErrorImpl]` is private -// // https://github.com/dtolnay/serde-yaml/issues/121 -// if layout.is_empty() { -// return Ok(LayoutFromYamlIntermediate::default()); -// } -// return Err(ConfigError::Serde(e)); -// }, -// Ok(config) => config, -// }; -// -// match layout { -// Some(layout) => { -// for tab in layout.tabs.clone() { -// tab.check()?; -// } -// Ok(layout) -// }, -// None => Ok(LayoutFromYamlIntermediate::default()), -// } -// } -// -// pub fn from_yaml(yaml: &str) -> LayoutFromYamlIntermediateResult { -// let layout: LayoutFromYamlIntermediate = match serde_yaml::from_str(yaml) { -// Err(e) => { -// // needs direct check, as `[ErrorImpl]` is private -// // https://github.com/dtolnay/serde-yaml/issues/121 -// if yaml.is_empty() { -// return Ok(LayoutFromYamlIntermediate::default()); -// } -// return Err(ConfigError::Serde(e)); -// }, -// Ok(config) => config, -// }; -// Ok(layout) -// } -// -// pub fn to_layout_and_config(&self) -> (LayoutFromYaml, Option) { -// let config = self.config.clone(); -// let layout = self.clone().into(); -// (layout, config) -// } -// -// pub fn from_path_or_default( -// layout: Option<&PathBuf>, -// layout_dir: Option, -// ) -> Option { -// layout -// .map(|layout| { -// // The way we determine where to look for the layout is similar to -// // how a path would look for an executable. -// // See the gh issue for more: https://github.com/zellij-org/zellij/issues/1412#issuecomment-1131559720 -// if layout.extension().is_some() || layout.components().count() > 1 { -// // We look localy! -// LayoutFromYamlIntermediate::from_path(layout) -// } else { -// // We look in the default dir -// LayoutFromYamlIntermediate::from_dir(layout, layout_dir.as_ref()) -// } -// }) -// .or_else(|| { -// Some(LayoutFromYamlIntermediate::from_dir( -// &std::path::PathBuf::from("default"), -// layout_dir.as_ref(), -// )) -// }) -// } -// -// // It wants to use Path here, but that doesn't compile. -// #[allow(clippy::ptr_arg)] -// pub fn from_dir( -// layout: &PathBuf, -// layout_dir: Option<&PathBuf>, -// ) -> LayoutFromYamlIntermediateResult { -// match layout_dir { -// Some(dir) => { -// let layout_path = &dir.join(layout); -// if layout_path.with_extension("yaml").exists() { -// Self::from_path(layout_path) -// } else { -// LayoutFromYamlIntermediate::from_default_assets(layout) -// } -// }, -// None => LayoutFromYamlIntermediate::from_default_assets(layout), -// } -// } -// // Currently still needed but on nightly -// // this is already possible: -// // HashMap<&'static str, Vec> -// pub fn from_default_assets(path: &Path) -> LayoutFromYamlIntermediateResult { -// match path.to_str() { -// Some("default") => Self::default_from_assets(), -// Some("strider") => Self::strider_from_assets(), -// Some("disable-status-bar") => Self::disable_status_from_assets(), -// Some("compact") => Self::compact_from_assets(), -// None | Some(_) => Err(ConfigError::IoPath( -// std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), -// path.into(), -// )), -// } -// } -// -// // TODO Deserialize the assets from bytes &[u8], -// // once serde-yaml supports zero-copy -// pub fn default_from_assets() -> LayoutFromYamlIntermediateResult { -// let layout: LayoutFromYamlIntermediate = -// serde_yaml::from_str(&String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// -// pub fn strider_from_assets() -> LayoutFromYamlIntermediateResult { -// let layout: LayoutFromYamlIntermediate = -// serde_yaml::from_str(&String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// -// pub fn disable_status_from_assets() -> LayoutFromYamlIntermediateResult { -// let layout: LayoutFromYamlIntermediate = -// serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// -// pub fn compact_from_assets() -> LayoutFromYamlIntermediateResult { -// let layout: LayoutFromYamlIntermediate = -// serde_yaml::from_str(&String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// } - -// type LayoutFromYamlResult = Result; - -// impl LayoutFromYaml { -// pub fn new(layout_path: &Path) -> LayoutFromYamlResult { -// let mut layout_file = File::open(&layout_path) -// .or_else(|_| File::open(&layout_path.with_extension("yaml"))) -// .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; -// -// let mut layout = String::new(); -// layout_file.read_to_string(&mut layout)?; -// let layout: Option = match serde_yaml::from_str(&layout) { -// Err(e) => { -// // needs direct check, as `[ErrorImpl]` is private -// // https://github.com/dtolnay/serde-yaml/issues/121 -// if layout.is_empty() { -// return Ok(LayoutFromYaml::default()); -// } -// return Err(ConfigError::Serde(e)); -// }, -// Ok(config) => config, -// }; -// -// match layout { -// Some(layout) => { -// for tab in layout.tabs.clone() { -// tab.check()?; -// } -// Ok(layout) -// }, -// None => Ok(LayoutFromYaml::default()), -// } -// } -// -// // It wants to use Path here, but that doesn't compile. -// #[allow(clippy::ptr_arg)] -// pub fn from_dir(layout: &PathBuf, layout_dir: Option<&PathBuf>) -> LayoutFromYamlResult { -// match layout_dir { -// Some(dir) => { -// Self::new(&dir.join(layout)).or_else(|_| Self::from_default_assets(layout)) -// }, -// None => Self::from_default_assets(layout), -// } -// } -// -// pub fn from_path_or_default( -// layout: Option<&PathBuf>, -// layout_path: Option<&PathBuf>, -// layout_dir: Option, -// ) -> Option { -// layout -// .map(|p| LayoutFromYaml::from_dir(p, layout_dir.as_ref())) -// .or_else(|| layout_path.map(|p| LayoutFromYaml::new(p))) -// .or_else(|| { -// Some(LayoutFromYaml::from_dir( -// &std::path::PathBuf::from("default"), -// layout_dir.as_ref(), -// )) -// }) -// } -// -// // Currently still needed but on nightly -// // this is already possible: -// // HashMap<&'static str, Vec> -// pub fn from_default_assets(path: &Path) -> LayoutFromYamlResult { -// match path.to_str() { -// Some("default") => Self::default_from_assets(), -// Some("strider") => Self::strider_from_assets(), -// Some("disable-status-bar") => Self::disable_status_from_assets(), -// None | Some(_) => Err(ConfigError::IoPath( -// std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), -// path.into(), -// )), -// } -// } -// -// // TODO Deserialize the assets from bytes &[u8], -// // once serde-yaml supports zero-copy -// pub fn default_from_assets() -> LayoutFromYamlResult { -// let layout: LayoutFromYaml = -// serde_yaml::from_str(&String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// -// pub fn strider_from_assets() -> LayoutFromYamlResult { -// let layout: LayoutFromYaml = -// serde_yaml::from_str(&String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// -// pub fn disable_status_from_assets() -> LayoutFromYamlResult { -// let layout: LayoutFromYaml = -// serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?; -// Ok(layout) -// } -// } - -// // The struct that is used to deserialize the session from -// // a yaml configuration file -// #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] -// #[serde(crate = "self::serde")] -// pub struct SessionFromYaml { -// pub name: Option, -// #[serde(default = "default_as_some_true")] -// pub attach: Option, -// } -// -// fn default_as_some_true() -> Option { -// Some(true) -// } - -// // The struct that carries the information template that is used to -// // construct the layout -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -// #[serde(crate = "self::serde")] -// pub struct LayoutTemplate { -// pub direction: SplitDirection, -// #[serde(default)] -// pub pane_name: Option, -// #[serde(default)] -// pub borderless: bool, -// #[serde(default)] -// pub parts: Vec, -// #[serde(default)] -// pub body: bool, -// pub split_size: Option, -// pub focus: Option, -// pub run: Option, -// } - -// impl LayoutTemplate { -// // Insert an optional `[TabLayout]` at the correct position -// pub fn insert_tab_layout(mut self, tab_layout: Option) -> Self { -// if self.body { -// return tab_layout.unwrap_or_default().into(); -// } -// for (i, part) in self.parts.clone().iter().enumerate() { -// if part.body { -// self.parts.push(tab_layout.unwrap_or_default().into()); -// self.parts.swap_remove(i); -// break; -// } -// // recurse -// let new_part = part.clone().insert_tab_layout(tab_layout.clone()); -// self.parts.push(new_part); -// self.parts.swap_remove(i); -// } -// self -// } -// -// fn from_vec_tab_layout(tab_layout: Vec) -> Vec { -// tab_layout -// .iter() -// .map(|tab_layout| Self::from(tab_layout.to_owned())) -// .collect() -// } -// } -// -// // The tab-layout struct used to specify each individual tab. -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -// #[serde(crate = "self::serde")] -// pub struct TabLayout { -// #[serde(default)] -// pub direction: SplitDirection, -// pub pane_name: Option, -// #[serde(default)] -// pub borderless: bool, -// #[serde(default)] -// pub parts: Vec, -// pub split_size: Option, -// #[serde(default)] -// pub name: String, -// pub focus: Option, -// pub run: Option, -// } -// -// impl TabLayout { -// fn check(&self) -> Result { -// for part in &self.parts { -// part.check()?; -// if !part.name.is_empty() { -// return Err(ConfigError::LayoutNameInTab(LayoutNameInTabError)); -// } -// } -// Ok(self.clone()) -// } -// } - impl Layout { pub fn with_one_pane() -> Self { let mut default_layout = Layout::default(); @@ -614,138 +250,11 @@ impl Layout { let mut kdl_layout = String::new(); layout_file.read_to_string(&mut kdl_layout)?; let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout, None) + Layout::from_kdl(&kdl_layout) } - pub fn from_kdl(kdl_layout: &KdlDocument, direction: Option) -> Result { - let mut tabs: Vec<(Option, Layout)> = vec![]; - let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; - let mut focused_tab_index = None; - fn parse_kdl_layout (kdl_layout: &KdlNode, tabs: &mut Vec<(Option, Layout)>, focused_tab_index: &mut Option) -> Result { - let borderless: bool = kdl_get_child_entry_bool_value!(kdl_layout, "borderless").unwrap_or(false); - let focus = kdl_get_child_entry_bool_value!(kdl_layout, "focus"); - let pane_name = kdl_get_child_entry_string_value!(kdl_layout, "name").map(|name| name.to_string()); - let mut split_size = None; - if let Some(string_split_size) = kdl_get_string_entry!(kdl_layout, "size") { - // "10%" => SplitSize::Percent(10) or 10 => SplitSize::Fixed(10) - split_size = Some(SplitSize::from_str(string_split_size)?); - } - if let Some(int_split_size) = kdl_get_int_entry!(kdl_layout, "size") { - split_size = Some(SplitSize::Fixed(int_split_size as usize)); - } - let mut run = None; - if let Some(kdl_command_block) = kdl_get_child!(kdl_layout, "command") { - run = Some(Run::Command(RunCommand::from_kdl(kdl_command_block)?)); - } - if let Some(kdl_plugin_block) = kdl_get_child!(kdl_layout, "plugin") { - if run.is_some() { - return Err(ConfigError::KdlParsingError("Cannot have both a command and a plugin block for a single pane".into())); - } - run = Some(Run::Plugin(RunPlugin::from_kdl(kdl_plugin_block)?)); - } - let mut direction = SplitDirection::default(); - let mut layout_parts = vec![]; - let mut tabs_index_in_children = None; - if let Some(kdl_parts) = kdl_get_child!(kdl_layout, "parts") { - let part_direction = kdl_get_string_entry!(kdl_parts, "direction").ok_or(ConfigError::KdlParsingError("no direction found for layout part".into()))?; - direction = SplitDirection::from_str(part_direction)?; - if let Some(children) = kdl_children_nodes!(kdl_parts) { - for (i, child) in children.iter().enumerate() { - let child_name = kdl_name!(child); - if child_name == "layout" { - layout_parts.push(parse_kdl_layout(&child, tabs, focused_tab_index)?); - } else if child_name == "tabs" { - tabs_index_in_children = Some(i); - if !tabs.is_empty() { - return Err(ConfigError::KdlParsingError(format!("Only one 'tabs' section allowed per layout..."))); - } - log::info!("child: {:?}", child); - if let Some(idx) = kdl_get_int_entry!(child, "focused_tab_index") { - log::info!("got idx: {:?}", idx); - let _ = focused_tab_index.insert(idx as usize); - } - match kdl_children_nodes!(child) { - Some(children) => { - for child in children { - let tab_name = kdl_get_string_entry!(child, "tab_name").map(|tab_name| tab_name.to_string()); - let tab_layout = parse_kdl_layout(&child, tabs, focused_tab_index)?; - tabs.push((tab_name, tab_layout)); - } - - }, - None => tabs.push((None, Layout::default())), - } - } else { - return Err(ConfigError::KdlParsingError(format!("Unknown layout part: {:?}", child_name))); - } - } - } - } - Ok(Layout { - direction, - pane_name, - parts: LayoutParts::Panes(layout_parts), - split_size, - run, - borderless, - focus, - tabs_index_in_children, - template: None, - session_name: None, - attach_to_session: false, - focused_tab_index: None, - }) - } - let mut base_layout = parse_kdl_layout(layout_node, &mut tabs, &mut focused_tab_index)?; - if !tabs.is_empty() { - let mut root_layout = Layout::default(); - root_layout.focused_tab_index = focused_tab_index; - let mut tab_parts: Vec<(Option, Layout)> = vec![]; - - for (i, (tab_name, tab)) in tabs.drain(..).enumerate() { - let mut layout_for_tab = base_layout.clone(); - layout_for_tab.insert_tab_layout(&tab); - layout_for_tab.tabs_index_in_children = None; - tab_parts.push((tab_name, layout_for_tab)); - - } - root_layout.parts = LayoutParts::Tabs(tab_parts); - let mut layout_template = base_layout.clone(); - layout_template.insert_tab_layout(&Layout::default()); - layout_template.tabs_index_in_children = None; - root_layout.template = Some(Box::new(layout_template)); - Ok(root_layout) - } else if base_layout.is_empty() { - // even a totally empty layout needs at least one pane - base_layout.parts = LayoutParts::Panes(vec![Layout::default()]); - Ok(base_layout) - } else { - Ok(base_layout) - } - + pub fn from_kdl(kdl_layout: &KdlDocument) -> Result { + KdlLayoutParser::new(&kdl_layout).parse() } -// let layout: Option = match serde_yaml::from_str(&layout) { -// Err(e) => { -// // needs direct check, as `[ErrorImpl]` is private -// // https://github.com/dtolnay/serde-yaml/issues/121 -// if layout.is_empty() { -// return Ok(Layout::default()); -// } -// return Err(ConfigError::Serde(e)); -// }, -// Ok(config) => config, -// }; -// -// match layout { -// Some(layout) => { -// for tab in layout.tabs.clone() { -// tab.check()?; -// } -// Ok(layout) -// }, -// None => Ok(LayoutFromYamlIntermediate::default()), -// } -// } - pub fn from_default_assets(path: &Path) -> Result { // TODO: ideally these should not be hard-coded // we should load layouts by name from the config @@ -765,25 +274,25 @@ impl Layout { pub fn default_from_assets() -> Result { let kdl_layout = String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?; let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout, None) + Layout::from_kdl(&kdl_layout) } pub fn strider_from_assets() -> Result { let kdl_layout = String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?; let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout, None) + Layout::from_kdl(&kdl_layout) } pub fn disable_status_from_assets() -> Result { let kdl_layout = String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?; let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout, None) + Layout::from_kdl(&kdl_layout) } pub fn compact_from_assets() -> Result { let kdl_layout = String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?; let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout, None) + Layout::from_kdl(&kdl_layout) } pub fn total_terminal_panes(&self) -> usize { @@ -831,7 +340,6 @@ impl Layout { total_borderless_panes }, LayoutParts::Tabs(tabs) => { - // let parts = tabs.values(); total_borderless_panes += tabs.iter().filter(|(_, p)| p.borderless).count(); for part in tabs { let (_part_name, part) = part; @@ -869,7 +377,6 @@ impl Layout { } pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(Layout, PaneGeom)> { - log::info!("position_panes_in_space..."); split_space(space, self) } @@ -882,41 +389,7 @@ impl Layout { pub fn new_tab(&self) -> Layout { match &self.template { Some(template) => *template.clone(), - None => self.clone() - } - } - pub fn insert_tab_layout(&mut self, tab_layout: &Layout) -> Result<(), &'static str> { - // this inserts a tab layout into a layout that doesn't yet have tabs, - // making sure to place it in the right place inside the existing template - match self.tabs_index_in_children { - Some(tabs_index_in_children) => { - match &mut self.parts { - LayoutParts::Panes(panes) => { - panes.insert(tabs_index_in_children, tab_layout.clone()); - Ok(()) - }, - LayoutParts::Tabs(tabs) => { - // TODO: CONTINUE HERE - if we insert tab and we have tabs, just add it - // here? (and below) - Err("Only top layout part can have a tabs block") - } - } - }, - None => { - match &mut self.parts { - LayoutParts::Panes(panes) => { - for child in panes.iter_mut() { - if let Ok(_) = child.insert_tab_layout(tab_layout) { - return Ok(()); - } - } - Err("no place to insert tabs here") - }, - LayoutParts::Tabs(_) => { - Err("Only top layout part can have a tabs block") - } - } - } + None => Layout::with_one_pane() } } @@ -941,25 +414,52 @@ impl Layout { } pub fn focused_tab_index(&self) -> Option { - log::info!("focused_tab_index? {:?}", self.focused_tab_index); self.focused_tab_index } -// fn from_vec_tab_layout(tab_layout: Vec) -> Result, ConfigError> { -// tab_layout -// .iter() -// .map(|tab_layout| Layout::try_from(tab_layout.to_owned())) -// .collect() -// } - -// fn from_vec_template_layout( -// layout_template: Vec, -// ) -> Result, ConfigError> { -// layout_template -// .iter() -// .map(|layout_template| Layout::try_from(layout_template.to_owned())) -// .collect() -// } + pub fn children_block_count(&self) -> usize { + let mut count = 0; + if self.external_children_index.is_some() { + count += 1; + } + match &self.parts { + LayoutParts::Tabs(tabs) => { + for tab in tabs { + count += tab.1.children_block_count(); + } + } + LayoutParts::Panes(panes) => { + for pane in panes { + count += pane.children_block_count(); + } + } + } + count + } + pub fn insert_children_layout(&mut self, children_layout: &mut Layout) -> Result { + // returns true if successfully inserted and false otherwise + let external_children_index = self.external_children_index; + match &mut self.parts { + LayoutParts::Tabs(tabs) => Err(ConfigError::KdlParsingError("Cannot insert child layout in tabs".into())), + LayoutParts::Panes(panes) => { + match external_children_index { + Some(external_children_index) => { + panes.insert(external_children_index, children_layout.clone()); + self.external_children_index = None; + Ok(true) + }, + None => { + for pane in panes.iter_mut() { + if pane.insert_children_layout(children_layout)? { + return Ok(true); + } + } + Ok(false) + } + } + } + } + } } fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { @@ -1014,18 +514,19 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); for (&size, part) in sizes.iter().zip(&*parts) { - let split_dimension = match size { + let mut split_dimension = match size { Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), Some(SplitSize::Fixed(size)) => Dimension::fixed(size), None => { + let total_fixed_size = split_dimension_space.as_usize(); let free_percent = if let Some(p) = split_dimension_space.as_percent() { p - sizes .iter() .map(|&s| { - if let Some(SplitSize::Percent(ip)) = s { - ip as f64 - } else { - 0.0 + match s { + Some(SplitSize::Percent(ip)) => ip as f64, + Some(SplitSize::Fixed(fixed)) => (fixed as f64 / total_fixed_size as f64) * 100.0, + _ => 0.0, } }) .sum::() @@ -1035,13 +536,7 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG Dimension::percent(free_percent / flex_parts as f64) }, }; - inherited_dimension.set_inner( - parts - .iter() - .map(|p| layout_size(!layout.direction, p)) - .max() - .unwrap(), - ); + split_dimension.adjust_inner(split_dimension_space.as_usize()); let geom = match layout.direction { SplitDirection::Vertical => PaneGeom { x: current_position, @@ -1057,7 +552,7 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG }, }; split_geom.push(geom); - current_position += layout_size(layout.direction, part); + current_position += split_dimension.as_usize(); } for (i, part) in parts.iter().enumerate() { @@ -1075,83 +570,6 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG panic!("tab layout should not have nested tabs"); } } -// let mut pane_positions = Vec::new(); -// // let sizes: Vec> = layout.parts.iter().map(|part| part.split_size).collect(); -// let sizes: Vec> = match layout.parts { -// LayoutParts::Panes(parts) => parts.iter().map(|part| part.split_size).collect(), -// LayoutParts::Tabs(tabs) => tabs.values().map(|part| part.split_size).collect(), -// }; -// -// let mut split_geom = Vec::new(); -// let (mut current_position, split_dimension_space, mut inherited_dimension) = -// match layout.direction { -// SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), -// SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), -// }; -// -// let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); -// -// let parts = match layout.parts { -// LayoutParts::Panes(parts) -// } -// for (&size, part) in sizes.iter().zip(&layout.parts) { -// let split_dimension = match size { -// Some(SplitSize::Percent(percent)) => Dimension::percent(percent), -// Some(SplitSize::Fixed(size)) => Dimension::fixed(size), -// None => { -// let free_percent = if let Some(p) = split_dimension_space.as_percent() { -// p - sizes -// .iter() -// .map(|&s| { -// if let Some(SplitSize::Percent(ip)) = s { -// ip -// } else { -// 0.0 -// } -// }) -// .sum::() -// } else { -// panic!("Implicit sizing within fixed-size panes is not supported"); -// }; -// Dimension::percent(free_percent / flex_parts as f64) -// }, -// }; -// inherited_dimension.set_inner( -// layout -// .parts -// .iter() -// .map(|p| layout_size(!layout.direction, p)) -// .max() -// .unwrap(), -// ); -// let geom = match layout.direction { -// SplitDirection::Vertical => PaneGeom { -// x: current_position, -// y: space_to_split.y, -// cols: split_dimension, -// rows: inherited_dimension, -// }, -// SplitDirection::Horizontal => PaneGeom { -// x: space_to_split.x, -// y: current_position, -// cols: inherited_dimension, -// rows: split_dimension, -// }, -// }; -// split_geom.push(geom); -// current_position += layout_size(layout.direction, part); -// } -// -// for (i, part) in layout.parts.iter().enumerate() { -// let part_position_and_size = split_geom.get(i).unwrap(); -// if !part.parts.is_empty() { -// let mut part_positions = split_space(part_position_and_size, part); -// pane_positions.append(&mut part_positions); -// } else { -// pane_positions.push((part.clone(), *part_position_and_size)); -// } -// } -// pane_positions } impl TryFrom for RunPluginLocation { @@ -1169,142 +587,6 @@ impl TryFrom for RunPluginLocation { } } -// impl TryFrom for Run { -// type Error = PluginsConfigError; -// -// fn try_from(run: RunFromYaml) -> Result { -// match run { -// RunFromYaml::Command(command) => Ok(Run::Command(command)), -// RunFromYaml::Plugin(plugin) => Ok(Run::Plugin(RunPlugin { -// _allow_exec_host_cmd: plugin._allow_exec_host_cmd, -// location: plugin.location.try_into()?, -// })), -// } -// } -// } -// -// impl From for LayoutFromYaml { -// fn from(layout_from_yaml_intermediate: LayoutFromYamlIntermediate) -> Self { -// Self { -// template: layout_from_yaml_intermediate.template, -// borderless: layout_from_yaml_intermediate.borderless, -// tabs: layout_from_yaml_intermediate.tabs, -// session: layout_from_yaml_intermediate.session, -// } -// } -// } -// -// impl From for LayoutFromYamlIntermediate { -// fn from(layout_from_yaml: LayoutFromYaml) -> Self { -// Self { -// template: layout_from_yaml.template, -// borderless: layout_from_yaml.borderless, -// tabs: layout_from_yaml.tabs, -// config: None, -// session: layout_from_yaml.session, -// } -// } -// } -// -// impl Default for LayoutFromYamlIntermediate { -// fn default() -> Self { -// LayoutFromYaml::default().into() -// } -// } -// -// impl TryFrom for Layout { -// type Error = ConfigError; -// -// fn try_from(tab: TabLayout) -> Result { -// Ok(Layout { -// direction: tab.direction, -// pane_name: tab.pane_name, -// borderless: tab.borderless, -// parts: LayoutParts::Panes(Self::from_vec_tab_layout(tab.parts)?), -// split_size: tab.split_size, -// focus: tab.focus, -// run: tab.run.map(Run::try_from).transpose()?, -// tabs_index_in_children: None, -// }) -// } -// } -// -// impl From for LayoutTemplate { -// fn from(tab: TabLayout) -> Self { -// Self { -// direction: tab.direction, -// pane_name: tab.pane_name, -// borderless: tab.borderless, -// parts: Self::from_vec_tab_layout(tab.parts), -// body: false, -// split_size: tab.split_size, -// focus: tab.focus, -// run: tab.run, -// } -// } -// } -// -// impl TryFrom for Layout { -// type Error = ConfigError; -// -// fn try_from(template: LayoutTemplate) -> Result { -// Ok(Layout { -// direction: template.direction, -// pane_name: template.pane_name, -// borderless: template.borderless, -// parts: LayoutParts::Panes(Self::from_vec_template_layout(template.parts)?), -// split_size: template.split_size, -// focus: template.focus, -// run: template -// .run -// .map(Run::try_from) -// // FIXME: This is just Result::transpose but that method is unstable, when it -// // stabalizes we should swap this out. -// .map_or(Ok(None), |r| r.map(Some))?, -// tabs_index_in_children: None, -// }) -// } -// } -// -// impl Default for TabLayout { -// fn default() -> Self { -// Self { -// direction: SplitDirection::Horizontal, -// borderless: false, -// parts: vec![], -// split_size: None, -// run: None, -// name: String::new(), -// pane_name: None, -// focus: None, -// } -// } -// } -// -// impl Default for LayoutTemplate { -// fn default() -> Self { -// Self { -// direction: SplitDirection::Horizontal, -// pane_name: None, -// body: false, -// borderless: false, -// parts: vec![LayoutTemplate { -// direction: SplitDirection::Horizontal, -// pane_name: None, -// body: true, -// borderless: false, -// split_size: None, -// focus: None, -// run: None, -// parts: vec![], -// }], -// split_size: None, -// focus: None, -// run: None, -// } -// } -// } - impl Default for SplitDirection { fn default() -> Self { SplitDirection::Horizontal diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index b75fdb05e7..8db1636990 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -1,5 +1,10 @@ +use crate::input::config::ConfigError; use super::super::layout::*; use std::convert::TryInto; +use insta::assert_snapshot; + +// println!("layout: {:#?}", layout); +// println!("expected_layout: {:#?}", expected_layout); fn layout_test_dir(layout: String) -> PathBuf { let root = Path::new(env!("CARGO_MANIFEST_DIR")); @@ -17,196 +22,107 @@ fn default_layout_dir(layout: String) -> PathBuf { fn empty_layout() { let kdl_layout = "layout"; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); - let expected_layout = Layout::with_one_pane(); - assert_eq!(layout, expected_layout); -} - -#[test] -fn layout_with_one_pane() { - let kdl_layout = r#" - layout { - parts direction="Horizontal" { - layout; - } - } - "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); - let expected_layout = Layout::with_one_pane(); - assert_eq!(layout, expected_layout); -} - -#[test] -fn layout_with_multiple_panes() { - let kdl_layout = r#" - layout { - parts direction="Horizontal" { - layout; - layout; - layout; - } - } - "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default() - ]), + template: Some(Box::new(Layout::with_one_pane())), ..Default::default() }; assert_eq!(layout, expected_layout); } #[test] -fn layout_with_nested_panes() { +fn layout_with_one_pane() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - parts direction="Vertical" { - layout; - layout; - } - } - layout { - parts direction="Horizontal" { - layout; - layout; - } - } - } + pane } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), - ..Default::default() - }, - Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), - ..Default::default() - } - ]), + template: Some(Box::new(Layout::with_one_pane())), ..Default::default() }; assert_eq!(layout, expected_layout); } #[test] -fn layout_with_multiple_nested_panes() { +fn layout_with_multiple_panes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - parts direction="Vertical" { - layout; - layout { - parts direction="Vertical" { - layout; - layout - } - } - } - } - layout; - layout { - parts direction="Horizontal" { - layout; - layout; - } - } - } + pane + pane + pane } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), - ..Default::default() - } - ]), - ..Default::default() - }, - Layout::default(), - Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), - ..Default::default() - } - ]), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default(), + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); } #[test] -fn layout_with_tabs() { +fn layout_with_nested_panes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - tabs { - layout; - } + pane split_direction="Vertical" { + pane + pane + } + pane { + pane + pane } } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Tabs(vec![ - (None, Layout::with_one_pane()), - ]), - template: Some(Box::new(Layout::with_one_pane())), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout { + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + } + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); } #[test] -fn layout_with_empty_tabs_block() { +fn layout_with_tabs() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - tabs; - } + tab } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, parts: LayoutParts::Tabs(vec![ (None, Layout::with_one_pane()), ]), @@ -220,42 +136,26 @@ fn layout_with_empty_tabs_block() { fn layout_with_nested_differing_tabs() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout; - tabs { - layout { - parts direction="Vertical" { - layout; - layout; - layout; - } - } - layout { - parts direction="Horizontal" { - layout; - layout; - layout; - } - } - } - layout; + tab split_direction="Vertical" { + pane + pane + pane + } + tab { + pane + pane } } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, parts: LayoutParts::Tabs(vec![ (None, Layout { - direction: SplitDirection::Horizontal, + direction: SplitDirection::Vertical, parts: LayoutParts::Panes(vec![ Layout::default(), - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![Layout::default(), Layout::default(), Layout::default()]), - ..Default::default() - }, + Layout::default(), Layout::default(), ]), ..Default::default() @@ -264,25 +164,12 @@ fn layout_with_nested_differing_tabs() { direction: SplitDirection::Horizontal, parts: LayoutParts::Panes(vec![ Layout::default(), - Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![Layout::default(), Layout::default(), Layout::default()]), - ..Default::default() - }, Layout::default(), ]), ..Default::default() }), ]), - template: Some(Box::new(Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), - ..Default::default() - })), + template: Some(Box::new(Layout::with_one_pane())), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -292,36 +179,36 @@ fn layout_with_nested_differing_tabs() { fn layout_with_panes_in_different_mixed_split_sizes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout size=1; - layout size="10%"; - layout; - layout size=2; - } + pane size=1; + pane size="10%"; + pane; + pane size=2; } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout { - split_size: Some(SplitSize::Fixed(1)), - ..Default::default() - }, - Layout { - split_size: Some(SplitSize::Percent(10)), - ..Default::default() - }, - Layout { - split_size: None, - ..Default::default() - }, - Layout { - split_size: Some(SplitSize::Fixed(2)), - ..Default::default() - }, - ]), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout { + split_size: Some(SplitSize::Fixed(1)), + ..Default::default() + }, + Layout { + split_size: Some(SplitSize::Percent(10)), + ..Default::default() + }, + Layout { + split_size: None, + ..Default::default() + }, + Layout { + split_size: Some(SplitSize::Fixed(2)), + ..Default::default() + }, + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -331,28 +218,26 @@ fn layout_with_panes_in_different_mixed_split_sizes() { fn layout_with_command_panes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - command { - cmd "htop" - } - } - } + pane command="htop" } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ + template: Some(Box::new( Layout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - ..Default::default() - })), + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + ..Default::default() + })), + ..Default::default() + } + ]), ..Default::default() }, - ]), + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -362,30 +247,27 @@ fn layout_with_command_panes() { fn layout_with_command_panes_and_cwd() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - command { - cmd "htop" - cwd "/path/to/my/cwd" - } - } - } + pane command="htop" cwd="/path/to/my/cwd" } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ + template: Some(Box::new( Layout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - cwd: Some(PathBuf::from("/path/to/my/cwd")), - ..Default::default() - })), + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + ..Default::default() + })), + ..Default::default() + } + ]), ..Default::default() }, - ]), + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -395,31 +277,30 @@ fn layout_with_command_panes_and_cwd() { fn layout_with_command_panes_and_cwd_and_args() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - command { - cmd "htop" - args "-h" "-v" - cwd "/path/to/my/cwd" - } - } + pane command="htop" cwd="/path/to/my/cwd" { + args "-h" "-v" } } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ + template: Some(Box::new( Layout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - args: vec![String::from("-h"), String::from("-v")], - cwd: Some(PathBuf::from("/path/to/my/cwd")), - })), + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + args: vec![String::from("-h"), String::from("-v")], + ..Default::default() + })), + ..Default::default() + } + ]), ..Default::default() }, - ]), + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -429,40 +310,36 @@ fn layout_with_command_panes_and_cwd_and_args() { fn layout_with_plugin_panes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - plugin { - location "zellij:tab-bar" - } - } - layout { - plugin { - location "file:/path/to/my/plugin.wasm" - } - } + pane { + plugin location="zellij:tab-bar" + } + pane { + plugin location="file:/path/to/my/plugin.wasm" } } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout { - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false, - })), - ..Default::default() - }, - Layout { - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::File(PathBuf::from("/path/to/my/plugin.wasm")), - _allow_exec_host_cmd: false, - })), - ..Default::default() - }, - ]), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout { + run: Some(Run::Plugin(RunPlugin { + location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + _allow_exec_host_cmd: false, + })), + ..Default::default() + }, + Layout { + run: Some(Run::Plugin(RunPlugin { + location: RunPluginLocation::File(PathBuf::from("/path/to/my/plugin.wasm")), + _allow_exec_host_cmd: false, + })), + ..Default::default() + }, + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -472,24 +349,21 @@ fn layout_with_plugin_panes() { fn layout_with_borderless_panes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - borderless true - } - layout - } + pane borderless=true } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - parts: LayoutParts::Panes(vec![ - Layout { - borderless: true, - ..Default::default() - }, - Layout::default() - ]), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout { + borderless: true, + ..Default::default() + }, + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -499,24 +373,21 @@ fn layout_with_borderless_panes() { fn layout_with_focused_panes() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - focus true - } - layout - } + pane focus=true } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - parts: LayoutParts::Panes(vec![ - Layout { - focus: Some(true), - ..Default::default() - }, - Layout::default() - ]), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout { + focus: Some(true), + ..Default::default() + }, + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -526,24 +397,21 @@ fn layout_with_focused_panes() { fn layout_with_pane_names() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - layout { - name "my awesome pane" - } - layout - } + pane name="my awesome pane" } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - parts: LayoutParts::Panes(vec![ - Layout { - pane_name: Some("my awesome pane".into()), - ..Default::default() - }, - Layout::default() - ]), + template: Some(Box::new(Layout { + parts: LayoutParts::Panes(vec![ + Layout { + pane_name: Some("my awesome pane".into()), + ..Default::default() + }, + ]), + ..Default::default() + })), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -553,38 +421,97 @@ fn layout_with_pane_names() { fn layout_with_tab_names() { let kdl_layout = r#" layout { - parts direction="Horizontal" { - tabs { - layout tab_name="my cool tab name 1" { - name "pane name inside tab 1" - } - layout tab_name="my cool tab name 2" { - name "pane name inside tab 2" - } - } - } + tab name="my cool tab name 1" + tab name="my cool tab name 2" } "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout, None).unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { direction: SplitDirection::Horizontal, parts: LayoutParts::Tabs(vec![ (Some("my cool tab name 1".into()), Layout { parts: LayoutParts::Panes(vec![ - Layout { - pane_name: Some("pane name inside tab 1".into()), - ..Default::default() - } + Layout::default() ]), ..Default::default() }), (Some("my cool tab name 2".into()), Layout { parts: LayoutParts::Panes(vec![ - Layout { - pane_name: Some("pane name inside tab 2".into()), + Layout::default() + ]), + ..Default::default() + }), + ]), + template: Some(Box::new(Layout::with_one_pane())), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_tab_templates() { + let kdl_layout = r#" + layout { + tab_template name="one-above-one-below" { + pane + children + pane + } + one-above-one-below name="my first tab" split_direction="Vertical" { + pane + pane + } + one-above-one-below name="my second tab" { + pane + pane + } + one-above-one-below + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Tabs(vec![ + (Some("my first tab".into()), Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), ..Default::default() - } + }, + Layout::default(), + ]), + ..Default::default() + }), + (Some("my second tab".into()), Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }), + (None, Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default(), ]), ..Default::default() }), @@ -595,864 +522,1374 @@ fn layout_with_tab_names() { assert_eq!(layout, expected_layout); } -// TODO: -// - session name - added to the config, TODO (CONTINUE HERE 19/08): add test for it and for -// attach_to_session in the config - DONE -// - focus for pane/tab -// - other stuff in layouts -// - merge layouts with config -// - default layout files -// - open new tab with layout template (maybe in tab_integration_tests?) -// - empty layout +#[test] +fn layout_with_default_tab_template() { + let kdl_layout = r#" + layout { + default_tab_template { + pane + children + pane + } + tab name="my first tab" split_direction="Vertical" { + pane + pane + } + tab name="my second tab" { + pane + pane + } + tab + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let expected_layout = Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Tabs(vec![ + (Some("my first tab".into()), Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }), + (Some("my second tab".into()), Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }), + (None, Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }), + ]), + template: Some(Box::new(Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default(), + ]), + ..Default::default() + })), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} -// TODO: CONTINUE HERE (18/08) -// - write tests similar to the config that will feed KDL into Layout::from_kdl and assert stuff -// about the layout - DONE -// - then bring these tests back -// TODO: BRING THESE TESTS BACK!! -// -// -// #[test] -// fn default_layout_is_ok() { -// let path = default_layout_dir("default.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// assert!(layout.is_ok()); -// } -// -// #[test] -// fn default_layout_has_one_tab() { -// let path = default_layout_dir("default.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// assert_eq!(layout_template.tabs.len(), 1); -// } -// -// #[test] -// fn default_layout_merged_correctly() { -// let path = default_layout_dir("default.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: true, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(1)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: true, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(2)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// ], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn default_layout_new_tab_correct() { -// let path = default_layout_dir("default.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: true, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(1)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: true, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(2)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// ], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn default_strider_layout_is_ok() { -// let path = default_layout_dir("strider.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// assert!(layout_from_yaml.is_ok()); -// } -// -// #[test] -// fn default_disable_status_layout_is_ok() { -// let path = default_layout_dir("disable-status-bar.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// assert!(layout_from_yaml.is_ok()); -// } -// -// #[test] -// fn default_disable_status_layout_has_no_tab() { -// let path = default_layout_dir("disable-status-bar.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// assert_eq!(layout_template.tabs.len(), 0); -// } -// -// #[test] -// fn three_panes_with_tab_is_ok() { -// let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// assert!(layout.is_ok()); -// } -// -// #[test] -// fn three_panes_with_tab_has_one_tab() { -// let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.unwrap(); -// assert_eq!(layout_template.tabs.len(), 1); -// } -// -// #[test] -// fn three_panes_with_tab_merged_correctly() { -// let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn three_panes_with_tab_new_tab_is_correct() { -// let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn three_panes_with_tab_and_default_plugins_is_ok() { -// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// assert!(layout.is_ok()); -// } -// -// #[test] -// fn three_panes_with_tab_and_default_plugins_has_one_tab() { -// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.unwrap(); -// assert_eq!(layout_template.tabs.len(), 1); -// } -// -// #[test] -// fn three_panes_with_tab_and_default_plugins_merged_correctly() { -// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(1)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(2)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// ], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { -// let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(1)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Fixed(2)), -// run: Some(Run::Plugin(RunPlugin { -// location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// _allow_exec_host_cmd: false, -// })), -// }, -// ], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn deeply_nested_tab_is_ok() { -// let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// assert!(layout.is_ok()); -// } -// -// #[test] -// fn deeply_nested_tab_has_one_tab() { -// let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.unwrap(); -// assert_eq!(layout_template.tabs.len(), 1); -// } -// -// #[test] -// fn deeply_nested_tab_merged_correctly() { -// let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(21)), -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(22)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(23)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(24)), -// run: None, -// }, -// ], -// split_size: Some(SplitSize::Percent(78)), -// run: None, -// }, -// ], -// split_size: Some(SplitSize::Percent(79)), -// run: None, -// }, -// ], -// split_size: Some(SplitSize::Percent(90)), -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(15)), -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(15)), -// run: None, -// }, -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(15)), -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn three_tabs_is_ok() { -// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// assert!(layout_from_yaml.is_ok()); -// } -// -// #[test] -// fn three_tabs_has_three_tabs() { -// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.unwrap(); -// assert_eq!(layout_template.tabs.len(), 3); -// } -// -// #[test] -// fn three_tabs_tab_one_merged_correctly() { -// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// let merged_layout = Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }; -// -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn three_tabs_tab_two_merged_correctly() { -// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[1].clone())); -// let merged_layout = Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// ], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }; -// -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn three_tabs_tab_three_merged_correctly() { -// let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// let layout = LayoutFromYaml::new(&path); -// let layout_template = layout.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[2].clone())); -// let merged_layout = Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![ -// Layout { -// direction: Direction::Vertical, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// ], -// split_size: Some(SplitSize::Percent(50)), -// run: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }, -// ], -// split_size: None, -// run: None, -// }; -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn no_tabs_is_ok() { -// let path = layout_test_dir("no-tab-section-specified.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// assert!(layout_from_yaml.is_ok()); -// } -// -// #[test] -// fn no_tabs_has_no_tabs() { -// let path = layout_test_dir("no-tab-section-specified.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.unwrap(); -// assert_eq!(layout_template.tabs.len(), 0); -// } -// -// #[test] -// fn no_tabs_merged_correctly() { -// let path = layout_test_dir("no-tab-section-specified.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![Layout { -// direction: Direction::Horizontal, -// borderless: false, -// pane_name: None, -// focus: None, -// parts: vec![], -// split_size: None, -// run: None, -// }], -// split_size: None, -// run: None, -// }; -// -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn no_layout_template_specified_is_ok() { -// let path = layout_test_dir("no-layout-template-specified.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// assert!(layout_from_yaml.is_ok()); -// } -// -// #[test] -// fn no_layout_template_has_one_tab() { -// let path = layout_test_dir("no-layout-template-specified.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.unwrap(); -// assert_eq!(layout_template.tabs.len(), 1); -// } -// -// #[test] -// fn no_layout_template_merged_correctly() { -// let path = layout_test_dir("no-layout-template-specified.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.as_ref().unwrap(); -// let tab_layout = layout_template -// .template -// .clone() -// .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// let merged_layout = Layout { -// direction: Direction::Horizontal, -// parts: vec![Layout { -// direction: Direction::Vertical, -// parts: vec![ -// Layout { -// direction: Direction::Horizontal, -// parts: vec![], -// split_size: None, -// run: None, -// borderless: false, -// pane_name: None, -// focus: None, -// }, -// Layout { -// direction: Direction::Horizontal, -// parts: vec![], -// split_size: None, -// run: None, -// borderless: false, -// pane_name: None, -// focus: None, -// }, -// ], -// split_size: None, -// run: None, -// borderless: false, -// pane_name: None, -// focus: None, -// }], -// split_size: None, -// run: None, -// borderless: false, -// pane_name: None, -// focus: None, -// }; -// -// assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// } -// -// #[test] -// fn session_name_to_layout_is_ok() { -// let path = layout_test_dir("session-name-to-layout.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// assert!(layout_from_yaml.is_ok()); -// } -// -// #[test] -// fn session_name_to_layout_has_name() { -// let path = layout_test_dir("session-name-to-layout.yaml".into()); -// let layout_from_yaml = LayoutFromYaml::new(&path); -// let layout_template = layout_from_yaml.unwrap(); -// let session_layout = layout_template.session; +#[test] +fn layout_with_pane_templates() { + let kdl_layout = r#" + layout { + pane_template name="left-and-right" split_direction="Vertical" { + pane + children + pane + } + left-and-right { + pane + } + left-and-right { + pane + pane + } + left-and-right split_direction="Vertical" { + pane + pane + } + left-and-right + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let expected_layout = Layout { + template: Some(Box::new(Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::with_one_pane(), + Layout::default(), + ]), + ..Default::default() + }, + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Horizontal, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }, + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + ]), + ..Default::default() + }, + Layout::default(), + ]), + ..Default::default() + }, + Layout { + direction: SplitDirection::Vertical, + parts: LayoutParts::Panes(vec![ + Layout::default(), + Layout::default(), + Layout::default(), + ]), + ..Default::default() + } + ]), + ..Default::default() + })), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + +#[test] +fn layout_with_tab_and_pane_templates() { + let kdl_layout = r#" + layout { + tab_template name="left-right-and-htop" { + left-and-right { + pane command="htop" + } + } + pane_template name="left-and-right" split_direction="Vertical" { + pane + children + pane + } + left-right-and-htop + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn layout_with_nested_pane_templates() { + let kdl_layout = r#" + layout { + pane_template name="left-and-right" split_direction="Vertical" { + pane + children + up-and-down + pane + } + pane_template name="up-and-down" split_direction="Horizontal" { + pane + pane + } + left-and-right + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn layout_with_nested_branched_pane_templates() { + let kdl_layout = r#" + layout { + pane_template name="left-and-right" split_direction="Vertical" { + pane + children + up-and-down + three-horizontal-panes + } + pane_template name="three-horizontal-panes" split_direction="Horizontal" { + pane + pane + pane + } + pane_template name="up-and-down" split_direction="Horizontal" { + pane + pane + } + left-and-right + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn circular_dependency_pane_templates_error() { + let kdl_layout = r#" + layout { + pane_template name="one" split_direction="Vertical" { + pane + children + two + } + pane_template name="two" split_direction="Horizontal" { + pane + three + pane + } + pane_template name="three" split_direction="Horizontal" { + one + } + one + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "circular dependency detected"); +} + +#[test] +fn children_not_as_first_child_of_tab_template() { + let kdl_layout = r#" + layout { + tab_template name="horizontal-with-vertical-top" { + pane split_direction="Vertical" { + pane + children + } + pane + } + horizontal-with-vertical-top name="my tab" { + pane + pane + } + horizontal-with-vertical-top + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn error_on_more_than_one_children_block_in_tab_template() { + let kdl_layout = r#" + layout { + tab_template name="horizontal-with-vertical-top" { + pane split_direction="Vertical" { + pane + children + } + children + pane + } + horizontal-with-vertical-top name="my tab" { + pane + pane + } + horizontal-with-vertical-top + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "error provided for more than one children block"); +} + +#[test] +fn children_not_as_first_child_of_pane_template() { + let kdl_layout = r#" + layout { + pane_template name="horizontal-with-vertical-top" { + pane split_direction="Vertical" { + pane + children + } + pane + } + horizontal-with-vertical-top name="my tab" { + pane + pane + } + horizontal-with-vertical-top + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn error_on_more_than_one_children_block_in_pane_template() { + let kdl_layout = r#" + layout { + pane_template name="horizontal-with-vertical-top" { + pane split_direction="Vertical" { + pane + children + } + children + pane + } + horizontal-with-vertical-top name="my tab" { + pane + pane + } + horizontal-with-vertical-top + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "error provided for more than one children block"); +} + +#[test] +fn combined_tab_and_pane_template_both_with_children() { + let kdl_layout = r#" + layout { + tab_template name="horizontal-with-vertical-top" { + vertical-sandwich { + pane name="middle" + } + children + } + pane_template name="vertical-sandwich" split_direction="Vertical" { + pane + children + pane + } + horizontal-with-vertical-top name="my tab" { + pane + pane + } + horizontal-with-vertical-top + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn cannot_define_tab_template_name_with_space() { + let kdl_layout = r#" + layout { + tab_template name="with space" { + pane + children + pane + } + pane + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "error provided for tab name with space"); +} + +#[test] +fn cannot_define_pane_template_name_with_space() { + let kdl_layout = r#" + layout { + pane_template name="with space" { + pane + children + pane + } + pane + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "error provided for tab name with space"); +} + +#[test] +fn cannot_define_panes_and_tabs_on_same_level() { + let kdl_layout = r#" + layout { + pane + tab { + pane + } + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "error provided for tab and pane on the same level"); +} + +#[test] +fn cannot_define_tab_template_names_as_keywords() { + let keywords = vec![ + "pane", + "layout", + "pane_template", + "tab_template", + "default_tab_template", + "command", + "plugin", + "children", + "tab" + ]; + for keyword in keywords { + let kdl_layout = format!(" + layout {{ + tab_template name=\"{}\" {{ + pane + children + pane + }} + pane + }} + ", keyword); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "{}", format!("error provided for tab template name with keyword: {}", keyword)); + } +} + +#[test] +fn cannot_define_pane_template_names_as_keywords() { + let keywords = vec![ + "pane", + "layout", + "pane_template", + "tab_template", + "command", + "plugin", + "children", + "tab" + ]; + for keyword in keywords { + let kdl_layout = format!(" + layout {{ + pane_template name=\"{}\" {{ + pane + children + pane + }} + pane + }} + ", keyword); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout); + assert!(layout.is_err(), "{}", format!("error provided for pane template name with keyword: {}", keyword)); + } + // TODO: CONTINUE HERE (28/08) + // - write this test - DONE + // - implement and write the rest of the tests below - DONE + // - refactor the kdl layouts part <-- YES! LET'S DO THIS! + // - add config and config overriding to layouts + +} + +// TODO more tests: +// - tab_templates with pane templates - DONE +// - nested pane templates - DONE +// - children not on first level of template - DONE // -// let expected_session = SessionFromYaml { -// name: Some(String::from("zellij-session")), -// attach: Some(true), -// }; +// errors: +// - template names with spaces - DONE +// - default tab template with name - N/A +// - panes and tabs on same level - DONE +// - cannot define template names that are identical to keywords - DONE + +// // TODO: +// // - session name - added to the config, TODO (CONTINUE HERE 19/08): add test for it and for +// // attach_to_session in the config - DONE +// // - focus for pane/tab +// // - other stuff in layouts +// // - merge layouts with config +// // - default layout files +// // - open new tab with layout template (maybe in tab_integration_tests?) +// // - empty layout // -// assert_eq!(expected_session, session_layout); -// } +// // TODO: CONTINUE HERE (18/08) +// // - write tests similar to the config that will feed KDL into Layout::from_kdl and assert stuff +// // about the layout - DONE +// // - then bring these tests back +// // TODO: BRING THESE TESTS BACK!! +// // +// // +// // #[test] +// // fn default_layout_is_ok() { +// // let path = default_layout_dir("default.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // assert!(layout.is_ok()); +// // } +// // +// // #[test] +// // fn default_layout_has_one_tab() { +// // let path = default_layout_dir("default.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // assert_eq!(layout_template.tabs.len(), 1); +// // } +// // +// // #[test] +// // fn default_layout_merged_correctly() { +// // let path = default_layout_dir("default.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: true, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(1)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: true, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(2)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn default_layout_new_tab_correct() { +// // let path = default_layout_dir("default.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: true, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(1)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: true, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(2)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn default_strider_layout_is_ok() { +// // let path = default_layout_dir("strider.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // assert!(layout_from_yaml.is_ok()); +// // } +// // +// // #[test] +// // fn default_disable_status_layout_is_ok() { +// // let path = default_layout_dir("disable-status-bar.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // assert!(layout_from_yaml.is_ok()); +// // } +// // +// // #[test] +// // fn default_disable_status_layout_has_no_tab() { +// // let path = default_layout_dir("disable-status-bar.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // assert_eq!(layout_template.tabs.len(), 0); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_is_ok() { +// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // assert!(layout.is_ok()); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_has_one_tab() { +// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.unwrap(); +// // assert_eq!(layout_template.tabs.len(), 1); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_merged_correctly() { +// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_new_tab_is_correct() { +// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_and_default_plugins_is_ok() { +// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // assert!(layout.is_ok()); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_and_default_plugins_has_one_tab() { +// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.unwrap(); +// // assert_eq!(layout_template.tabs.len(), 1); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_and_default_plugins_merged_correctly() { +// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(1)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(2)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { +// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(1)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Fixed(2)), +// // run: Some(Run::Plugin(RunPlugin { +// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), +// // _allow_exec_host_cmd: false, +// // })), +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn deeply_nested_tab_is_ok() { +// // let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // assert!(layout.is_ok()); +// // } +// // +// // #[test] +// // fn deeply_nested_tab_has_one_tab() { +// // let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.unwrap(); +// // assert_eq!(layout_template.tabs.len(), 1); +// // } +// // +// // #[test] +// // fn deeply_nested_tab_merged_correctly() { +// // let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(21)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(22)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(23)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(24)), +// // run: None, +// // }, +// // ], +// // split_size: Some(SplitSize::Percent(78)), +// // run: None, +// // }, +// // ], +// // split_size: Some(SplitSize::Percent(79)), +// // run: None, +// // }, +// // ], +// // split_size: Some(SplitSize::Percent(90)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(15)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(15)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(15)), +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn three_tabs_is_ok() { +// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // assert!(layout_from_yaml.is_ok()); +// // } +// // +// // #[test] +// // fn three_tabs_has_three_tabs() { +// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.unwrap(); +// // assert_eq!(layout_template.tabs.len(), 3); +// // } +// // +// // #[test] +// // fn three_tabs_tab_one_merged_correctly() { +// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn three_tabs_tab_two_merged_correctly() { +// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[1].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn three_tabs_tab_three_merged_correctly() { +// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); +// // let layout = LayoutFromYaml::new(&path); +// // let layout_template = layout.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[2].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![ +// // Layout { +// // direction: Direction::Vertical, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: Some(SplitSize::Percent(50)), +// // run: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // }; +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn no_tabs_is_ok() { +// // let path = layout_test_dir("no-tab-section-specified.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // assert!(layout_from_yaml.is_ok()); +// // } +// // +// // #[test] +// // fn no_tabs_has_no_tabs() { +// // let path = layout_test_dir("no-tab-section-specified.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.unwrap(); +// // assert_eq!(layout_template.tabs.len(), 0); +// // } +// // +// // #[test] +// // fn no_tabs_merged_correctly() { +// // let path = layout_test_dir("no-tab-section-specified.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![Layout { +// // direction: Direction::Horizontal, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // }], +// // split_size: None, +// // run: None, +// // }; +// // +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn no_layout_template_specified_is_ok() { +// // let path = layout_test_dir("no-layout-template-specified.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // assert!(layout_from_yaml.is_ok()); +// // } +// // +// // #[test] +// // fn no_layout_template_has_one_tab() { +// // let path = layout_test_dir("no-layout-template-specified.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.unwrap(); +// // assert_eq!(layout_template.tabs.len(), 1); +// // } +// // +// // #[test] +// // fn no_layout_template_merged_correctly() { +// // let path = layout_test_dir("no-layout-template-specified.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.as_ref().unwrap(); +// // let tab_layout = layout_template +// // .template +// // .clone() +// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); +// // let merged_layout = Layout { +// // direction: Direction::Horizontal, +// // parts: vec![Layout { +// // direction: Direction::Vertical, +// // parts: vec![ +// // Layout { +// // direction: Direction::Horizontal, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // }, +// // Layout { +// // direction: Direction::Horizontal, +// // parts: vec![], +// // split_size: None, +// // run: None, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // }, +// // ], +// // split_size: None, +// // run: None, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // }], +// // split_size: None, +// // run: None, +// // borderless: false, +// // pane_name: None, +// // focus: None, +// // }; +// // +// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); +// // } +// // +// // #[test] +// // fn session_name_to_layout_is_ok() { +// // let path = layout_test_dir("session-name-to-layout.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // assert!(layout_from_yaml.is_ok()); +// // } +// // +// // #[test] +// // fn session_name_to_layout_has_name() { +// // let path = layout_test_dir("session-name-to-layout.yaml".into()); +// // let layout_from_yaml = LayoutFromYaml::new(&path); +// // let layout_template = layout_from_yaml.unwrap(); +// // let session_layout = layout_template.session; +// // +// // let expected_session = SessionFromYaml { +// // name: Some(String::from("zellij-session")), +// // attach: Some(true), +// // }; +// // +// // assert_eq!(expected_session, session_layout); +// // } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap new file mode 100644 index 0000000000..0492ad9cb2 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap @@ -0,0 +1,222 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 856 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: Some( + 1, + ), + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap new file mode 100644 index 0000000000..555f17850c --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap @@ -0,0 +1,247 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 809 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Tabs( + [ + ( + Some( + "my tab", + ), + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), + ( + None, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: Some( + 1, + ), + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap new file mode 100644 index 0000000000..25fc00846d --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap @@ -0,0 +1,331 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 907 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Tabs( + [ + ( + Some( + "my tab", + ), + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: Some( + "middle", + ), + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), + ( + None, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: Some( + "middle", + ), + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap new file mode 100644 index 0000000000..9cd6f2f73e --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap @@ -0,0 +1,202 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 760 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap new file mode 100644 index 0000000000..b88cf0bc36 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap @@ -0,0 +1,153 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 733 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap new file mode 100644 index 0000000000..d098d82de0 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap @@ -0,0 +1,166 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 711 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Tabs( + [ + ( + None, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Vertical, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: Some( + Command( + RunCommand { + command: "htop", + args: [], + cwd: None, + }, + ), + ), + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + session_name: None, + attach_to_session: false, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs new file mode 100644 index 0000000000..436c8ba693 --- /dev/null +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -0,0 +1,528 @@ +//! The layout system. +// Layouts have been moved from [`zellij-server`] to +// [`zellij-utils`] in order to provide more helpful +// error messages to the user until a more general +// logging system is in place. +// In case there is a logging system in place evaluate, +// if [`zellij-utils`], or [`zellij-server`] is a proper +// place. +// If plugins should be able to depend on the layout system +// then [`zellij-utils`] could be a proper place. +use crate::{ + input::{ + command::RunCommand, + config::{ConfigError, LayoutNameInTabError}, + layout::{Layout, LayoutParts, SplitDirection, Run, RunPlugin, RunPluginLocation, SplitSize}, + plugins::{PluginTag, PluginsConfigError}, + }, + pane_size::{Dimension, PaneGeom}, + setup, +}; + +use kdl::*; + +use std::str::FromStr; +use std::collections::{HashMap, HashSet}; + +use crate::{ + kdl_children, + kdl_string_arguments, + kdl_children_nodes, + kdl_name, + kdl_document_name, + kdl_get_string_entry, + kdl_get_int_entry, + kdl_get_child_entry_bool_value, + kdl_get_child_entry_string_value, + kdl_get_child, + kdl_get_bool_property_or_child_value, + kdl_get_string_property_or_child_value, + kdl_get_int_property_or_child_value, +}; + +// use super::{ +// // config::ConfigFromYaml, +// plugins::{PluginTag, PluginsConfigError}, +// }; +use serde::{Deserialize, Serialize}; +use std::convert::{TryFrom, TryInto}; +use std::vec::Vec; +use std::{ + cmp::max, + fmt, fs, + ops::Not, + path::{Path, PathBuf}, +}; +use std::{fs::File, io::prelude::*}; +use url::Url; + +pub struct KdlLayoutParser <'a>{ + kdl_layout: &'a KdlDocument, + tab_templates: HashMap, + pane_templates: HashMap, + default_tab_template: Option, +} + +impl <'a>KdlLayoutParser <'a> { + pub fn new(kdl_layout: &'a KdlDocument) -> Self { + KdlLayoutParser { + kdl_layout, + tab_templates: HashMap::new(), + pane_templates: HashMap::new(), + default_tab_template: None, + } + } + fn is_a_reserved_word(&self, word: &str) -> bool { + word == "pane" || + word == "layout" || + word == "pane_template" || + word == "tab_template" || + word == "default_tab_template" || + word == "command" || + word == "plugin" || + word == "children" || + word == "tab" + } + fn assert_legal_node_name(&self, name: &str) -> Result<(), ConfigError> { + if name.contains(char::is_whitespace) { + Err(ConfigError::KdlParsingError(format!("Node names ({}) cannot contain whitespace.", name))) + } else if self.is_a_reserved_word(&name) { + Err(ConfigError::KdlParsingError(format!("Node name '{}' is a reserved word.", name))) + } else { + Ok(()) + } + } + fn parse_split_size(&self, kdl_node: &KdlNode) -> Result, ConfigError> { + if let Some(size) = kdl_get_string_property_or_child_value!(kdl_node, "size") { + Ok(Some(SplitSize::from_str(size)?)) + } else if let Some(size) = kdl_get_int_property_or_child_value!(kdl_node, "size") { + Ok(Some(SplitSize::Fixed(size as usize))) + } else { + Ok(None) + } + } + fn parse_plugin_block(&self, plugin_block: &KdlNode) -> Result, ConfigError> { + let _allow_exec_host_cmd = kdl_get_bool_property_or_child_value!(plugin_block, "_allow_exec_host_cmd").unwrap_or(false); + let string_url = kdl_get_string_property_or_child_value!(plugin_block, "location").ok_or(ConfigError::KdlParsingError("Plugins must have a location".into()))?; + let url = Url::parse(string_url).map_err(|e| ConfigError::KdlParsingError(format!("Failed to aprse url: {:?}", e)))?; + let location = RunPluginLocation::try_from(url)?; + Ok(Some(Run::Plugin(RunPlugin { + _allow_exec_host_cmd, + location + }))) + } + fn parse_pane_command(&self, pane_node: &KdlNode) -> Result, ConfigError> { + let command = kdl_get_string_property_or_child_value!(pane_node, "command").map(|c| PathBuf::from(c)); + let cwd = kdl_get_string_property_or_child_value!(pane_node, "cwd").map(|c| PathBuf::from(c)); + let args = match kdl_get_child!(pane_node, "args") { + Some(kdl_args) => Some(kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect()), + None => None, + }; + match (command, cwd, args) { + (None, Some(_cwd), _) => { + Err(ConfigError::KdlParsingError("Cwd can only be set if a command was specified".into())) + } + (None, _, Some(_args)) => { + Err(ConfigError::KdlParsingError("Args can only be set if a command was specified".into())) + } + (Some(command), cwd, args) => { + Ok(Some(Run::Command(RunCommand { + command, + args: args.unwrap_or_else(|| vec![]), + cwd + }))) + } + _ => Ok(None) + } + } + fn parse_command_or_plugin_block(&self, kdl_node: &KdlNode) -> Result, ConfigError> { + let mut run = self.parse_pane_command(kdl_node)?; + if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") { + if run.is_some() { + return Err(ConfigError::KdlParsingError("Cannot have both a command and a plugin block for a single pane".into())); + } + run = self.parse_plugin_block(plugin_block)?; + } + Ok(run) + } + fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { + let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); + let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); + let pane_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|name| name.to_string()); + let split_size = self.parse_split_size(kdl_node)?; + let run = self.parse_command_or_plugin_block(kdl_node)?; + let direction = self.parse_split_direction(kdl_node)?; + let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, + None => (None, vec![]) + }; + Ok(Layout { + borderless: borderless.unwrap_or_default(), + focus, + pane_name, + split_size, + run, + direction, + external_children_index, + parts: LayoutParts::Panes(pane_parts), + ..Default::default() + }) + } + fn parse_pane_node_with_template(&self, kdl_node: &KdlNode, mut pane_layout: Layout) -> Result { + let direction = self.parse_split_direction(kdl_node)?; + match kdl_children_nodes!(kdl_node) { + Some(children) => { + let (_, mut child_panes) = self.parse_child_pane_nodes_for_pane(&children)?; + if child_panes.is_empty() { + child_panes.push(Layout::default()); + } + let child_panes_layout = Layout { + direction, + parts: LayoutParts::Panes(child_panes), + ..Default::default() + }; + self.assert_one_children_block(&pane_layout)?; + self.insert_layout_children_or_error(&mut pane_layout, child_panes_layout)?; + }, + None => { + if let Some(index_of_children) = pane_layout.external_children_index { + pane_layout.parts.insert_pane(index_of_children, Layout::default())?; + } + } + } + pane_layout.external_children_index = None; + Ok(pane_layout) + } + fn parse_split_direction(&self, kdl_node: &KdlNode) -> Result { + Ok(match kdl_get_string_entry!(kdl_node, "split_direction") { + Some(direction) => SplitDirection::from_str(direction)?, + None => SplitDirection::default(), + }) + } + fn parse_pane_template_node(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name + let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()).ok_or(ConfigError::KdlParsingError("Pane templates must have a name".into()))?; + self.assert_legal_node_name(&template_name)?; + let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); + let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); + let split_size = self.parse_split_size(kdl_node)?; + let run = self.parse_command_or_plugin_block(kdl_node)?; + let direction = self.parse_split_direction(kdl_node)?; + let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, + None => (None, vec![]) + }; + self.pane_templates.insert(template_name, Layout { + borderless: borderless.unwrap_or_default(), + focus, + split_size, + run, + direction, + external_children_index, + parts: LayoutParts::Panes(pane_parts), + ..Default::default() + }); + Ok(()) + } + fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(Option, Layout), ConfigError> { // String is the tab name + match self.default_tab_template.as_ref().map(|t| t.clone()) { + Some(default_tab_template) => { + self.parse_tab_node_with_template(kdl_node, default_tab_template) + }, + None => { + let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + Some(direction) => SplitDirection::from_str(direction)?, + None => SplitDirection::default(), + }; + let pane_parts = match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_tab(children)?, + None => vec![Layout::default()], + }; + Ok((tab_name, Layout { + direction, + parts: LayoutParts::Panes(pane_parts), + ..Default::default() + })) + } + } + } + fn parse_child_pane_nodes_for_tab(&self, children: &[KdlNode]) -> Result, ConfigError> { + let mut nodes = vec![]; + for child in children { + if kdl_name!(child) == "pane" { + nodes.push(self.parse_pane_node(child)?); + } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { + nodes.push(self.parse_pane_node_with_template(child, pane_template)?); + } + } + if nodes.is_empty() { + nodes.push(Layout::default()); + } + Ok(nodes) + } + fn parse_child_pane_nodes_for_pane(&self, children: &[KdlNode]) -> Result<(Option, Vec), ConfigError> { // usize is external_children_index + let mut external_children_index = None; + let mut nodes = vec![]; + for (i, child) in children.iter().enumerate() { + if kdl_name!(child) == "pane" { + nodes.push(self.parse_pane_node(child)?); + } else if kdl_name!(child) == "children" { + external_children_index = Some(i); + } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { + nodes.push(self.parse_pane_node_with_template(child, pane_template)?); + } + } + Ok((external_children_index, nodes)) + } + fn assert_one_children_block(&self, layout: &Layout) -> Result<(), ConfigError> { + let children_block_count = layout.children_block_count(); + if children_block_count != 1 { + return Err(ConfigError::KdlParsingError(format!("Layout has {} children blocks, only 1 is allowed", children_block_count))); + } + Ok(()) + } + fn insert_layout_children_or_error(&self, layout: &mut Layout, mut child_panes_layout: Layout) -> Result<(), ConfigError> { + let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?; + if !successfully_inserted { + Err(ConfigError::KdlParsingError("This tab template does not have children".into())) + } else { + Ok(()) + } + } + fn parse_tab_node_with_template(&mut self, kdl_node: &KdlNode, mut tab_layout: Layout) -> Result<(Option, Layout), ConfigError> { // String is the tab name + let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + Some(direction) => SplitDirection::from_str(direction)?, + None => SplitDirection::default(), + }; + match kdl_children_nodes!(kdl_node) { + Some(children) => { + let child_panes = self.parse_child_pane_nodes_for_tab(children)?; + let child_panes_layout = Layout { + direction, + parts: LayoutParts::Panes(child_panes), + ..Default::default() + }; + self.assert_one_children_block(&tab_layout)?; + self.insert_layout_children_or_error(&mut tab_layout, child_panes_layout)?; + }, + None => { + if let Some(index_of_children) = tab_layout.external_children_index { + tab_layout.parts.insert_pane(index_of_children, Layout::default())?; + } + } + } + tab_layout.external_children_index = None; + Ok((tab_name, tab_layout)) + } + fn populate_one_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name + let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()).ok_or(ConfigError::KdlParsingError("Tab templates must have a name".into()))?; + self.assert_legal_node_name(&template_name)?; + self.tab_templates.insert(template_name, self.parse_tab_template_node(kdl_node)?); + Ok(()) + } + fn populate_default_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name + self.default_tab_template = Some(self.parse_tab_template_node(kdl_node)?); + Ok(()) + } + fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result { + let direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + Some(direction) => SplitDirection::from_str(direction)?, + None => SplitDirection::default(), + }; + let mut pane_parts = vec![]; + let mut external_children_index = None; + if let Some(children) = kdl_children_nodes!(kdl_node) { + for (i, child) in children.iter().enumerate() { + if kdl_name!(child) == "pane" { + pane_parts.push(self.parse_pane_node(child)?); + } else if kdl_name!(child) == "children" { + external_children_index = Some(i); + } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { + pane_parts.push(self.parse_pane_node_with_template(child, pane_template)?); + } + } + } + if pane_parts.is_empty() { + pane_parts.push(Layout::default()); + } + Ok(Layout { + direction, + parts: LayoutParts::Panes(pane_parts), + external_children_index, + ..Default::default() + }) + } + fn default_template(&self) -> Result, ConfigError> { + match &self.default_tab_template { + Some(template) => { + let mut template = template.clone(); + if let Some(children_index) = template.external_children_index { + template.parts.insert_pane(children_index, Layout::default())?; + } + template.external_children_index = None; + Ok(Some(template)) + }, + None => Ok(None) + } + } + pub fn get_pane_template_dependency_tree(&self, kdl_children: &'a [KdlNode]) -> Result>, ConfigError> { + let mut dependency_tree = HashMap::new(); + for child in kdl_children { + if kdl_name!(child) == "pane_template" { + let template_name = kdl_get_string_property_or_child_value!(child, "name") + .ok_or(ConfigError::KdlParsingError("Pane templates must have a name".into()))?; + let mut template_children = HashSet::new(); + self.get_pane_template_dependencies(child, &mut template_children)?; + dependency_tree.insert(template_name, template_children); + } + } + Ok(dependency_tree) + } + fn get_pane_template_dependencies(&self, node: &'a KdlNode, all_dependencies: &mut HashSet<&'a str>) -> Result<(), ConfigError> { + if let Some(children) = kdl_children_nodes!(node) { + for child in children { + let child_name = kdl_name!(child); + if child_name == "pane" { + self.get_pane_template_dependencies(child, all_dependencies)?; + } else if !self.is_a_reserved_word(child_name) { + all_dependencies.insert(child_name); + self.get_pane_template_dependencies(child, all_dependencies)?; + } + } + } + Ok(()) + } + pub fn parse_pane_template_by_name(&mut self, pane_template_name: &str, kdl_children: &[KdlNode]) -> Result<(), ConfigError> { + for child in kdl_children.iter() { + let child_name = kdl_name!(child); + if child_name == "pane_template" { + let child_name = kdl_get_string_property_or_child_value!(child, "name"); + if child_name == Some(pane_template_name) { + self.parse_pane_template_node(child)?; + } + } + }; + Ok(()) + } + fn populate_pane_templates(&mut self, layout_children: &[KdlNode]) -> Result<(), ConfigError> { + let mut pane_template_dependency_tree = self.get_pane_template_dependency_tree(layout_children)?; + let mut pane_template_names_to_parse: Vec<&str> = vec![]; + // toposort the dependency tree so that we parse the pane_templates before their + // dependencies + while !pane_template_dependency_tree.is_empty() { + let mut candidates: Vec<&str> = vec![]; + for (pane_tempalte, dependencies) in pane_template_dependency_tree.iter() { + if dependencies.is_empty() { + candidates.push(pane_tempalte); + } + } + if candidates.is_empty() { + return Err(ConfigError::KdlParsingError("Circular dependency detected between pane templates.".into())); + } + for candidate_to_remove in candidates { + pane_template_dependency_tree.remove(candidate_to_remove); + for (_pane_tempalte, dependencies) in pane_template_dependency_tree.iter_mut() { + dependencies.remove(candidate_to_remove); + } + pane_template_names_to_parse.push(candidate_to_remove); + } + } + // once we've toposorted, parse the sorted list in order + for pane_template_name in pane_template_names_to_parse { + self.parse_pane_template_by_name(pane_template_name, &layout_children)?; + } + Ok(()) + } + fn populate_tab_templates(&mut self, layout_children: &[KdlNode]) -> Result<(), ConfigError> { + for child in layout_children.iter() { + let child_name = kdl_name!(child); + if child_name == "tab_template" { + self.populate_one_tab_template(child)?; + } else if child_name == "default_tab_template" { + self.populate_default_tab_template(child)?; + } + } + Ok(()) + } + fn layout_with_tabs(&self, tabs: Vec<(Option, Layout)>) -> Result { + let template = self.default_template()?.unwrap_or_else(|| Layout::with_one_pane()); + Ok(Layout { + parts: LayoutParts::Tabs(tabs), + template: Some(Box::new(template)), + ..Default::default() + }) + } + fn layout_with_one_tab(&self, panes: Vec) -> Result { + let main_tab_layout = Layout { + parts: LayoutParts::Panes(panes.clone()), + ..Default::default() + }; + let default_template = self.default_template()?; + let parts = if default_template.is_none() { + // in this case, the layout will be created as the default template and we don't need + // to explicitly place it in the first tab + LayoutParts::default() + } else { + LayoutParts::Tabs(vec![(None, main_tab_layout.clone())]) + }; + let template = default_template.unwrap_or_else(|| main_tab_layout.clone()); + // create a layout with one tab that has these child panes + Ok(Layout { + parts, + template: Some(Box::new(template)), + ..Default::default() + }) + } + fn layout_with_one_pane(&self) -> Result { + let template = self.default_template()?.unwrap_or_else(|| Layout::with_one_pane()); + Ok(Layout { + template: Some(Box::new(template)), + ..Default::default() + }) + } + fn populate_layout_child(&mut self, child: &KdlNode, child_tabs: &mut Vec<(Option, Layout)>, child_panes: &mut Vec) -> Result<(), ConfigError> { + let child_name = kdl_name!(child); + if child_name == "pane" { + if !child_tabs.is_empty() { + return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + } + child_panes.push(self.parse_pane_node(child)?); + } else if child_name == "tab" { + if !child_panes.is_empty() { + return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + } + child_tabs.push(self.parse_tab_node(child)?); + } else if let Some(tab_template) = self.tab_templates.get(child_name).cloned() { + if !child_panes.is_empty() { + return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + } + child_tabs.push(self.parse_tab_node_with_template(child, tab_template)?); + } else if let Some(pane_template) = self.pane_templates.get(child_name).cloned() { + if !child_tabs.is_empty() { + return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + } + child_panes.push(self.parse_pane_node_with_template(child, pane_template)?); + } + Ok(()) + } + pub fn parse(&mut self) -> Result { + let layout_node = self.kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; + let mut child_tabs = vec![]; + let mut child_panes = vec![]; + if let Some(children) = kdl_children_nodes!(layout_node) { + self.populate_pane_templates(children)?; + self.populate_tab_templates(children)?; + for child in children { + self.populate_layout_child(child, &mut child_tabs, &mut child_panes)?; + } + } + if !child_tabs.is_empty() { + self.layout_with_tabs(child_tabs) + } else if !child_panes.is_empty() { + self.layout_with_one_tab(child_panes) + } else { + self.layout_with_one_pane() + } + } +} diff --git a/zellij-utils/src/kdl.rs b/zellij-utils/src/kdl/mod.rs similarity index 96% rename from zellij-utils/src/kdl.rs rename to zellij-utils/src/kdl/mod.rs index 76ef94f118..a7e04928d0 100644 --- a/zellij-utils/src/kdl.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,5 +1,5 @@ -//! Definition of the actions that can be bound to keys. +pub mod kdl_layout_parser; // use super::layout::TabLayout; use crate::data::{InputMode, Key, CharOrArrow, PaletteColor}; use crate::input::options::{Options, OnForceClose, Clipboard}; @@ -602,6 +602,45 @@ macro_rules! kdl_get_child_entry_string_value { } } +#[macro_export] +macro_rules! kdl_get_bool_property_or_child_value { + ( $kdl_node:expr, $name:expr ) => { + $kdl_node.get($name) + .and_then(|e| e.value().as_bool()) + .or_else(|| $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_bool()) + ) + } +} + +#[macro_export] +macro_rules! kdl_get_string_property_or_child_value { + ( $kdl_node:expr, $name:expr ) => { + $kdl_node.get($name) + .and_then(|e| e.value().as_string()) + .or_else(|| $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_string()) + ) + } +} + +#[macro_export] +macro_rules! kdl_get_int_property_or_child_value { + ( $kdl_node:expr, $name:expr ) => { + $kdl_node.get($name) + .and_then(|e| e.value().as_i64()) + .or_else(|| $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_i64()) + ) + } +} + #[macro_export] macro_rules! kdl_get_string_entry { ( $kdl_node:expr, $entry_name:expr ) => { diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 2a9f54c00d..789df446fd 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -83,6 +83,17 @@ impl Dimension { self.inner = inner; } + pub fn adjust_inner(&mut self, full_size: usize) { + match self.constraint { + Constraint::Percent(percent) => { + self.set_inner(((percent / 100.0) * full_size as f64) as usize); + } + Constraint::Fixed(fixed_size) => { + self.set_inner(fixed_size); + } + } + } + pub fn is_fixed(&self) -> bool { matches!(self.constraint, Constraint::Fixed(_)) } From c26356ebc0c4bf6d10c9338657bfeb935426e37e Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 1 Sep 2022 11:12:24 +0200 Subject: [PATCH 15/55] refactor(kdl): move stuff around --- src/commands.rs | 4 +- zellij-utils/assets/config/default.kdl | 116 +- zellij-utils/assets/layouts/default.kdl | 256 ++ zellij-utils/src/cli.rs | 9 + zellij-utils/src/data.rs | 46 +- zellij-utils/src/envs.rs | 30 +- zellij-utils/src/input/actions.rs | 23 + zellij-utils/src/input/command.rs | 18 - zellij-utils/src/input/config.rs | 38 - zellij-utils/src/input/keybinds.rs | 76 +- zellij-utils/src/input/layout.rs | 110 +- zellij-utils/src/input/options.rs | 69 +- zellij-utils/src/input/plugins.rs | 44 +- zellij-utils/src/input/theme.rs | 63 +- ...n_not_as_first_child_of_pane_template.snap | 26 - ...en_not_as_first_child_of_tab_template.snap | 28 - ..._and_pane_template_both_with_children.snap | 38 - ...t_with_nested_branched_pane_templates.snap | 26 +- ...st__layout_with_nested_pane_templates.snap | 20 +- ...t__layout_with_tab_and_pane_templates.snap | 20 +- zellij-utils/src/kdl/mod.rs | 241 +- zellij-utils/src/setup.rs | 330 +- ...cli_arguments_override_config_options.snap | 27 + ...i_arguments_override_layout_options-2.snap | 49 + ...cli_arguments_override_layout_options.snap | 27 + ...efault_config_with_no_cli_arguments-2.snap | 107 + ...efault_config_with_no_cli_arguments-3.snap | 25 + ..._default_config_with_no_cli_arguments.snap | 2945 +++++++++++++++ ...out_env_vars_override_config_env_vars.snap | 2949 +++++++++++++++ ...out_keybinds_override_config_keybinds.snap | 149 + ...out_options_override_config_options-2.snap | 49 + ...ayout_options_override_config_options.snap | 27 + ...ayout_plugins_override_config_plugins.snap | 2959 +++++++++++++++ ..._layout_themes_override_config_themes.snap | 3249 +++++++++++++++++ ..._ui_config_overrides_config_ui_config.snap | 2945 +++++++++++++++ .../test-fixtures/config-with-env-vars.kdl | 4 + .../config-with-keybindings-config.kdl | 11 + .../config-with-plugins-config.kdl | 6 + .../config-with-themes-config.kdl | 28 + .../test-fixtures/config-with-ui-config.kdl | 5 + .../test-fixtures/layout-with-env-vars.kdl | 5 + .../layout-with-keybindings-config.kdl | 11 + .../src/test-fixtures/layout-with-options.kdl | 2 + .../layout-with-plugins-config.kdl | 5 + .../layout-with-themes-config.kdl | 30 + .../test-fixtures/layout-with-ui-config.kdl | 6 + 46 files changed, 16486 insertions(+), 765 deletions(-) create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap create mode 100644 zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap create mode 100644 zellij-utils/src/test-fixtures/config-with-env-vars.kdl create mode 100644 zellij-utils/src/test-fixtures/config-with-keybindings-config.kdl create mode 100644 zellij-utils/src/test-fixtures/config-with-plugins-config.kdl create mode 100644 zellij-utils/src/test-fixtures/config-with-themes-config.kdl create mode 100644 zellij-utils/src/test-fixtures/config-with-ui-config.kdl create mode 100644 zellij-utils/src/test-fixtures/layout-with-env-vars.kdl create mode 100644 zellij-utils/src/test-fixtures/layout-with-keybindings-config.kdl create mode 100644 zellij-utils/src/test-fixtures/layout-with-options.kdl create mode 100644 zellij-utils/src/test-fixtures/layout-with-plugins-config.kdl create mode 100644 zellij-utils/src/test-fixtures/layout-with-themes-config.kdl create mode 100644 zellij-utils/src/test-fixtures/layout-with-ui-config.kdl diff --git a/src/commands.rs b/src/commands.rs index 5f9935e8ea..f47c60ab97 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -152,7 +152,7 @@ fn attach_with_fake_client(opts: zellij_utils::cli::CliArgs, name: &str) { let action = format!("[{}]", action); match zellij_utils::serde_yaml::from_str::(&action).into_diagnostic() { Ok(parsed) => { - let (config, _, config_options) = match Setup::from_options(&opts) { + let (config, _, config_options) = match Setup::from_cli_args(&opts) { Ok(results) => results, Err(e) => { eprintln!("{}", e); @@ -249,7 +249,7 @@ fn attach_with_session_name( } pub(crate) fn start_client(opts: CliArgs) { - let (config, layout, config_options) = match Setup::from_options(&opts) { + let (config, layout, config_options) = match Setup::from_cli_args(&opts) { Ok(results) => results, Err(e) => { eprintln!("{}", e); diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 7015ea3e8d..dd79ae15b7 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -4,7 +4,7 @@ keybinds { bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl s" { SwitchToMode "Search"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } @@ -32,7 +32,7 @@ keybinds { bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } @@ -56,7 +56,7 @@ keybinds { bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } @@ -89,7 +89,7 @@ keybinds { bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl q" { Quit; } bind "n" "Tab" { MovePane; } @@ -110,7 +110,7 @@ keybinds { bind "Ctrl p" { SwitchToMode "Pane"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl b" { SwitchToMode "Tmux"; } bind "Ctrl o" { SwitchToMode "Session"; } @@ -139,7 +139,7 @@ keybinds { bind "Alt +" "Alt =" { Resize "Increase"; } bind "Alt -" { Resize "Decrease"; } } - search { + scroll { bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl g" { SwitchToMode "Locked"; } @@ -149,6 +149,7 @@ keybinds { bind "Ctrl o" { SwitchToMode "Session"; } bind "Ctrl n" { SwitchToMode "Resize"; } bind "e" { EditScrollback; SwitchToMode "Normal"; } + bind "s" { SwitchToMode "EnterSearch"; } bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } bind "Ctrl q" { Quit; } bind "j" "Down" { ScrollDown; } @@ -167,6 +168,48 @@ keybinds { // uncomment this and adjust key if using copy_on_select=false // bind "Alt c" { Copy; } } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl t" { SwitchToMode "Tab"; } + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl p" { SwitchToMode "Pane"; } + bind "Ctrl h" { SwitchToMode "Move"; } + bind "Ctrl b" { SwitchToMode "Tmux"; } + bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl q" { Quit; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt +" "Alt =" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "n" { Search "down"; } + bind "p" { Search "up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "Search"; } + bind "Ctrl c" "Esc" { SwitchToMode "Scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" {Resize "Increase"; } + bind "Alt +" {Resize "Increase"; } + bind "Alt -" {Resize "Decrease"; } + } renametab { bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } @@ -198,7 +241,7 @@ keybinds { bind "Ctrl b" { SwitchToMode "Tmux"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } + bind "Ctrl s" { SwitchToMode "Scroll"; } bind "Ctrl q" { Quit; } bind "d" { Detach; } bind "Alt n" { NewPane; } @@ -216,7 +259,7 @@ keybinds { bind "Ctrl h" { SwitchToMode "Move"; } bind "Ctrl t" { SwitchToMode "Tab"; } bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "[" { SwitchToMode "Search"; } + bind "[" { SwitchToMode "Scroll"; } bind "Ctrl q" { Quit; } bind "Ctrl b" { Write 2; SwitchToMode "Normal"; } bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } @@ -247,6 +290,13 @@ keybinds { } } +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + // Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP // eg. when terminal window with an active zellij session is closed // Options: @@ -273,7 +323,7 @@ keybinds { // Choose the theme that is specified in the themes section. // For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes // Default: default -// theme "default" +// theme "dracula" // Choose the mode that zellij uses when starting up. // Default: normal @@ -324,51 +374,3 @@ keybinds { // or should each user have their own cursor (false) // Default: false // mirror_session true - -themes { - dracula { - // From https://github.com/dracula/zellij - 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 - fg 248 248 242 - cyan 139 233 253 - black 0 0 0 - white 255 255 255 - } - nord { - fg "#D8DEE9" - bg "#2E3440" - black "#3B4252" - red "#BF616A" - green "#A3BE8C" - yellow "#EBCB8B" - blue "#81A1C1" - magenta "#B48EAD" - cyan "#88C0D0" - white "#E5E9F0" - orange "#D08770" - } -} - -plugins { - tab-bar { path "tab-bar"; } - status-bar { path "status-bar"; } - strider { path "strider"; } - compact-bar { path "compact-bar"; } -} - -ui { - pane_frames { - rounded_corners false - } -} - -env { - RUST_BACKTRACE 1 - foo "bar" -} diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index 4434b29b1f..f8b4595d7b 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -7,3 +7,259 @@ layout { plugin location="zellij:status-bar" } } +// keybinds { +// normal { +// bind "Ctrl g" { SwitchToMode "Pane"; } +// bind "Ctrl a" { SwitchToMode "Locked"; } +// } +// } +// pane_frames false +// default_mode "pane" +// themes { +// dracula { +// // From https://github.com/dracula/zellij +// 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 +// fg 248 248 242 +// cyan 139 233 253 +// black 0 0 0 +// white 255 255 255 +// } +// } +// env { +// RUST_BACKTRACE 2 +// bar "foo" +// } +// layout { +// parts direction="Horizontal" { +// layout size=1 { +// borderless true +// plugin { +// location "zellij:tab-bar" +// } +// } +// tabs; +// layout size=2 { +// borderless true +// plugin { +// location "zellij:status-bar" +// } +// } +// } +// } + +// TODO: CONTINUE HERE (20/08) - consider implementing this +// with tab templates +// layout { +// pane_template name="htop" { +// pane command="htop" +// vertical-sandwich +// foopane +// } +// pane_template name="vertical-sandwich" split_direction="vertical" { +// pane +// children +// pane +// } +// pane_template name="foopane" { +// pane name="foo" +// htop-vertical-sandwich +// } +// pane_template name="htop-vertical-sandwich" split_direction="vertical" { +// pane +// htop +// pane +// } +// htop-vertical-sandwich +// // htop-vertical-sandwich +// // htop +// // pane +// // pane +// } + +// layout { +// tab_template name="horizontal-with-vertical-top" { +// pane split_direction="Vertical" { +// pane +// children +// } +// my-pane-template { +// pane { +// pane +// children +// } +// pane +// } +// pane +// } +// pane_template name="my-pane_template" { +// pane +// children +// } +// horizontal-with-vertical-top name="my tab" { +// pane +// pane +// } +// horizontal-with-vertical-top +// } + + +// layout { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar" +// } +// pane +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } +// +// layout { +// pane_template name="python_pane" { +// pane command="./my-python.script.py" +// } +// default_tab_template { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar" +// } +// children +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } +// tab_template name="my-tab-template" { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar" +// } +// children +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } +// pane_template name="my-cool-pane-tempalte" split_direction="Horiztonal" { +// pane split_direction="Vertical" { +// pane +// children +// pane +// } +// pane +// } +// my-tab-template split_direction="Vertical" { +// my-cool-pane-template { +// pane +// pane +// } +// pane command="htop" +// } +// my-tab-template split_direction="Horizontal" { +// pane +// python_pane +// pane command="bandwhich" +// } +// } + +// layout { +// // tab { +// // pane size=1 borderless=true { +// // plugin location="zellij:tab-bar" +// // } +// // pane +// // pane size=2 borderless=true { +// // plugin location="zellij:status-bar" +// // } +// // } +// tab_template name="not-default" { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar" +// } +// children +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } +// not-default name="first tab" { +// pane +// pane +// } +// not-default name="second tab" split_direction="Vertical" { +// pane +// pane +// } +// not-default +// } + +// layout split_direction="Horizontal" { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar"; +// } +// pane split_direction="Vertical" { +// pane size=1 +// pane +// pane size=2 +// } +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } + +// // without templates +// layout { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar"; +// } +// pane +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } + +// // with pane templates +// layout { +// tab_template name="tab_with_plugins" { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar"; +// } +// children; +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } +// pane_template name="three-vertical-panes" split_direction="Vertical" { +// pane +// pane +// pane +// } +// pane_template name="two_panes_with_child" split_direction="Vertical" { +// pane +// children +// pane +// } +// pane_template name="htop" { +// command "htop" +// cwd "/tmp" +// args "-v" "-h" +// } +// tabs { +// tab_with_plugins name="some tab name" split_direction="Horizontal" focus=true { +// three-vertical-panes +// pane command="diskonaut" cwd="/" +// pane { +// command "bandwhich" +// focus true +// } +// two_panes_with_child { +// htop +// } +// } +// tab_with_plugins +// tab_with_plugins { +// plugin +// htop focused=true +// } +// } +// } diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index a947f94343..908b296761 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -46,6 +46,15 @@ pub struct CliArgs { pub debug: bool, } +impl CliArgs { + pub fn should_clean_config(&self) -> bool { + match &self.command { + Some(Command::Setup(ref setup)) => setup.clean, + _ => false, + } + } +} + #[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] pub enum Command { /// Change the behaviour of zellij diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 8604782020..717803c25a 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -244,7 +244,7 @@ pub enum Event { } /// Describes the different input modes, which change the way that keystrokes will be interpreted. -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize, ArgEnum)] +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize, ArgEnum, PartialOrd, Ord)] pub enum InputMode { /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading /// to other modes @@ -292,26 +292,26 @@ pub enum InputMode { Tmux, } -impl TryFrom<&str> for InputMode { - type Error = String; - fn try_from(mode: &str) -> Result { - match mode { - "normal" | "Normal" => Ok(InputMode::Normal), - "locked" | "Locked" => Ok(InputMode::Locked), - "resize" | "Resize" => Ok(InputMode::Resize), - "pane" | "Pane" => Ok(InputMode::Pane), - "tab" | "Tab" => Ok(InputMode::Tab), - "search" | "Search" => Ok(InputMode::Search), - "renametab" | "RenameTab" => Ok(InputMode::RenameTab), - "renamepane" | "RenamePane" => Ok(InputMode::RenamePane), - "session" | "Session" => Ok(InputMode::Session), - "move" | "Move" => Ok(InputMode::Move), - "prompt" | "Prompt" => Ok(InputMode::Prompt), - "tmux" | "Tmux" => Ok(InputMode::Tmux), - _ => Err(format!("Unrecognized mode: {}", mode)), - } - } -} +// impl TryFrom<&str> for InputMode { +// type Error = String; +// fn try_from(mode: &str) -> Result { +// match mode { +// "normal" | "Normal" => Ok(InputMode::Normal), +// "locked" | "Locked" => Ok(InputMode::Locked), +// "resize" | "Resize" => Ok(InputMode::Resize), +// "pane" | "Pane" => Ok(InputMode::Pane), +// "tab" | "Tab" => Ok(InputMode::Tab), +// "search" | "Search" => Ok(InputMode::Search), +// "renametab" | "RenameTab" => Ok(InputMode::RenameTab), +// "renamepane" | "RenamePane" => Ok(InputMode::RenamePane), +// "session" | "Session" => Ok(InputMode::Session), +// "move" | "Move" => Ok(InputMode::Move), +// "prompt" | "Prompt" => Ok(InputMode::Prompt), +// "tmux" | "Tmux" => Ok(InputMode::Tmux), +// _ => Err(format!("Unrecognized mode: {}", mode)), +// } +// } +// } impl Default for InputMode { fn default() -> InputMode { @@ -352,12 +352,14 @@ impl FromStr for InputMode { "pane" | "Pane" => Ok(InputMode::Pane), "tab" | "Tab" => Ok(InputMode::Tab), "search" | "Search" => Ok(InputMode::Search), + "scroll" | "Scroll" => Ok(InputMode::Scroll), "renametab" | "RenameTab" => Ok(InputMode::RenameTab), "renamepane" | "RenamePane" => Ok(InputMode::RenamePane), "session" | "Session" => Ok(InputMode::Session), "move" | "Move" => Ok(InputMode::Move), "prompt" | "Prompt" => Ok(InputMode::Prompt), "tmux" | "Tmux" => Ok(InputMode::Tmux), + "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch), e => Err(format!("unknown mode \"{}\"", e).into()), } } @@ -452,7 +454,7 @@ pub struct PluginIds { } /// Tag used to identify the plugin in layout and config yaml files -#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)] pub struct PluginTag(String); impl PluginTag { diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index 7ff1fd4949..e0df159b68 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -5,10 +5,12 @@ use crate::input::config::ConfigError; use kdl::KdlNode; use crate::{kdl_children_nodes_or_error, kdl_name, kdl_first_entry_as_string, kdl_first_entry_as_i64}; use std::{ - collections::HashMap, + collections::{HashMap, BTreeMap}, env::{set_var, var}, }; +use std::fmt; + pub const ZELLIJ_ENV_KEY: &str = "ZELLIJ"; pub fn get_zellij() -> Result { Ok(var(ZELLIJ_ENV_KEY)?) @@ -37,11 +39,21 @@ pub fn get_socket_dir() -> Result { } /// Manage ENVIRONMENT VARIABLES from the configuration and the layout files -#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Default, Clone, PartialEq, Serialize, Deserialize)] pub struct EnvironmentVariables { env: HashMap, } +impl fmt::Debug for EnvironmentVariables { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut stable_sorted = BTreeMap::new(); + for (env_var_name, env_var_value) in self.env.iter() { + stable_sorted.insert(env_var_name, env_var_value); + } + write!(f, "{:#?}", stable_sorted) + } +} + impl EnvironmentVariables { /// Merges two structs, keys from `other` supersede keys from `self` pub fn merge(&self, other: Self) -> Self { @@ -54,20 +66,6 @@ impl EnvironmentVariables { env: data } } - pub fn from_kdl(kdl_env_variables: &KdlNode) -> Result { - let mut env: HashMap = HashMap::new(); - for env_var in kdl_children_nodes_or_error!(kdl_env_variables, "empty env variable block") { - let env_var_name = kdl_name!(env_var); - let env_var_str_value = kdl_first_entry_as_string!(env_var).map(|s| format!("{}", s.to_string())); - let env_var_int_value = kdl_first_entry_as_i64!(env_var).map(|s| format!("{}", s.to_string())); - let env_var_value = env_var_str_value - .or(env_var_int_value) - .ok_or::>(format!("Failed to parse env var: {:?}", env_var_name).into())?; - env.insert(env_var_name.into(), env_var_value); - } - Ok(EnvironmentVariables::from_data(env)) - } - /// Set all the ENVIRONMENT VARIABLES, that are configured /// in the configuration and layout files pub fn set_vars(&self) { diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index c6cc9ac4b2..05a47be517 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -61,6 +61,17 @@ pub enum SearchDirection { Up, } +impl FromStr for SearchDirection { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "Down" | "down" => Ok(SearchDirection::Down), + "Up" | "up" => Ok(SearchDirection::Up), + _ => Err(format!("Failed to parse SearchDirection. Unknown SearchDirection: {}", s)), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum SearchOption { CaseSensitivity, @@ -68,6 +79,18 @@ pub enum SearchOption { Wrap, } +impl FromStr for SearchOption { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "CaseSensitivity" | "casesensitivity" | "Casesensitivity" => Ok(SearchOption::CaseSensitivity), + "WholeWord" | "wholeword" | "Wholeword" => Ok(SearchOption::WholeWord), + "Wrap" | "wrap" => Ok(SearchOption::Wrap), + _ => Err(format!("Failed to parse SearchOption. Unknown SearchOption: {}", s)), + } + } +} + // As these actions are bound to the default config, please // do take care when refactoring - or renaming. // They might need to be adjusted in the default config diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs index 1c3a0f6019..e8df9637ac 100644 --- a/zellij-utils/src/input/command.rs +++ b/zellij-utils/src/input/command.rs @@ -23,24 +23,6 @@ pub struct RunCommand { pub cwd: Option, } -impl RunCommand { - pub fn from_kdl(kdl_node: &KdlNode) -> Result { - let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or(ConfigError::KdlParsingError("Command must have a cmd value".into()))?); - let cwd = kdl_get_child_entry_string_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); - let args = match kdl_get_child!(kdl_node, "args") { - Some(kdl_args) => { - kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect() - }, - None => vec![] - }; - Ok(RunCommand { - command, - args, - cwd, - }) - } -} - /// Intermediate representation #[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)] pub struct RunCommandAction { diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index dad9478816..55b2260400 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -31,20 +31,6 @@ const DEFAULT_CONFIG_FILE_NAME: &str = "config.kdl"; type ConfigResult = Result; -// /// Intermediate deserialization config struct -// #[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq)] -// pub struct ConfigFromYaml { -// #[serde(flatten)] -// pub options: Option, -// pub keybinds: Option, -// pub themes: Option, -// #[serde(flatten)] -// pub env: Option, -// #[serde(default)] -// pub plugins: PluginsConfigFromYaml, -// pub ui: Option, -// } - /// Main configuration. #[derive(Debug, Clone, PartialEq, Default)] pub struct Config { @@ -128,30 +114,6 @@ impl Config { None => self.themes.get_theme("default").map(|theme| theme.palette) } } - pub fn from_kdl(kdl_config: &str, base_config: Option) -> ConfigResult { - let mut config = base_config.unwrap_or_else(|| Config::default()); - let kdl_config: KdlDocument = kdl_config.parse()?; - // TODO: handle cases where we have more than one of these blocks (eg. two "keybinds") - // this should give an informative parsing error - if let Some(kdl_keybinds) = kdl_config.get("keybinds") { - config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds)?; - } - config.options = Options::from_kdl(&kdl_config); - if let Some(kdl_themes) = kdl_config.get("themes") { - config.themes = Themes::from_kdl(kdl_themes)?; - } - if let Some(kdl_plugin_config) = kdl_config.get("plugins") { - config.plugins = PluginsConfig::from_kdl(kdl_plugin_config)?; - } - if let Some(kdl_ui_config) = kdl_config.get("ui") { - config.ui = UiConfig::from_kdl(&kdl_ui_config)?; - } - if let Some(env_config) = kdl_config.get("env") { - config.env = EnvironmentVariables::from_kdl(&env_config)?; - } - Ok(config) - } - /// Gets default configuration from assets // TODO Deserialize the Config from bytes &[u8], // once serde-yaml supports zero-copy diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index 02a92c19ce..715b79c490 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -10,10 +10,27 @@ use crate::data::{InputMode, Key, KeybindsVec}; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use std::fmt; /// Used in the config struct -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)] +#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] pub struct Keybinds(pub HashMap>>); + +impl fmt::Debug for Keybinds { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut stable_sorted = BTreeMap::new(); + for (mode, keybinds) in self.0.iter() { + let mut stable_sorted_mode_keybinds = BTreeMap::new(); + for (key, actions) in keybinds { + stable_sorted_mode_keybinds.insert(key, actions); + } + stable_sorted.insert(mode, stable_sorted_mode_keybinds); + } + write!(f, "{:#?}", stable_sorted) + } +} + + // pub struct Keybinds(HashMap); #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct ModeKeybinds(BTreeMap>); @@ -70,63 +87,6 @@ enum Unbind { } impl Keybinds { - fn bind_actions_for_each_key(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ - let keys: Vec = keys_from_kdl!(key_block); - let actions: Vec = actions_from_kdl!(key_block); - for key in keys { - input_mode_keybinds.insert(key, actions.clone()); - } - Ok(()) - } - fn unbind_keys(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ - let keys: Vec = keys_from_kdl!(key_block); - for key in keys { - input_mode_keybinds.remove(&key); - } - Ok(()) - } - fn unbind_keys_in_all_modes(global_unbind: &KdlNode, keybinds_from_config: &mut Keybinds) -> Result<(), ConfigError> { - let keys: Vec = keys_from_kdl!(global_unbind); - for mode in keybinds_from_config.0.values_mut() { - for key in &keys { - mode.remove(&key); - } - } - Ok(()) - } - fn input_mode_keybindings <'a>(mode: &KdlNode, keybinds_from_config: &'a mut Keybinds) -> Result<&'a mut HashMap>, ConfigError> { - let mode_name = kdl_name!(mode); - let input_mode = InputMode::from_str(mode_name)?; - let input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&input_mode); - let clear_defaults_for_mode = kdl_arg_is_truthy!(mode, "clear-defaults"); - if clear_defaults_for_mode { - input_mode_keybinds.clear(); - } - Ok(input_mode_keybinds) - } - pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result { - let clear_defaults = kdl_arg_is_truthy!(kdl_keybinds, "clear-defaults"); - let mut keybinds_from_config = if clear_defaults { Keybinds::default() } else { base_keybinds }; - for mode in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { - if kdl_name!(mode) == "unbind" { - continue; - } - let mut input_mode_keybinds = Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?; - let bind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); - let unbind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); - for key_block in bind_nodes { - Keybinds::bind_actions_for_each_key(key_block, &mut input_mode_keybinds)?; - } - // we loop twice so that the unbinds always happen after the binds - for key_block in unbind_nodes { - Keybinds::unbind_keys(key_block, &mut input_mode_keybinds)?; - } - } - if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { - Keybinds::unbind_keys_in_all_modes(global_unbind, &mut keybinds_from_config)?; - }; - Ok(keybinds_from_config) - } pub fn get_actions_for_key_in_mode(&self, mode: &InputMode, key: &Key) -> Option<&Vec> { self.0.get(mode) .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key)) diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 1ab58f550a..eaf16ccf7d 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -11,36 +11,18 @@ use crate::{ input::{ command::RunCommand, - config::{ConfigError, LayoutNameInTabError}, + config::{Config, ConfigError, LayoutNameInTabError}, }, pane_size::{Dimension, PaneGeom}, setup, }; -use crate::kdl::kdl_layout_parser::KdlLayoutParser; use kdl::*; use std::str::FromStr; use std::collections::{HashMap, HashSet}; -use crate::{ - kdl_children, - kdl_string_arguments, - kdl_children_nodes, - kdl_name, - kdl_document_name, - kdl_get_string_entry, - kdl_get_int_entry, - kdl_get_child_entry_bool_value, - kdl_get_child_entry_string_value, - kdl_get_child, - kdl_get_bool_property_or_child_value, - kdl_get_string_property_or_child_value, - kdl_get_int_property_or_child_value, -}; - use super::{ - // config::ConfigFromYaml, plugins::{PluginTag, PluginsConfigError}, }; use serde::{Deserialize, Serialize}; @@ -95,19 +77,6 @@ pub struct RunPlugin { pub location: RunPluginLocation, } -impl RunPlugin { - pub fn from_kdl(kdl_node: &KdlNode) -> Result { - let _allow_exec_host_cmd = kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false); - let string_url = kdl_get_child_entry_string_value!(kdl_node, "location").ok_or(ConfigError::KdlParsingError("Plugins must have a location".into()))?; - let url = Url::parse(string_url).map_err(|e| ConfigError::KdlParsingError(format!("Failed to aprse url: {:?}", e)))?; - let location = RunPluginLocation::try_from(url)?; - Ok(RunPlugin { - _allow_exec_host_cmd, - location, - }) - } -} - #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum RunPluginLocation { File(PathBuf), @@ -154,9 +123,6 @@ pub struct Layout { #[serde(default)] pub borderless: bool, pub focus: Option, - // TODO: move these elsewhere? - pub session_name: Option, - pub attach_to_session: bool, pub external_children_index: Option, pub focused_tab_index: Option, pub template: Option>, @@ -204,7 +170,7 @@ impl Layout { default_layout.parts = LayoutParts::Panes(vec![Layout::default()]); default_layout } - pub fn from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result { + pub fn stringified_from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result { match layout_path { Some(layout_path) => { // The way we determine where to look for the layout is similar to @@ -212,87 +178,81 @@ impl Layout { // See the gh issue for more: https://github.com/zellij-org/zellij/issues/1412#issuecomment-1131559720 if layout_path.extension().is_some() || layout_path.components().count() > 1 { // We look localy! - Layout::from_path(layout_path) + Layout::stringified_from_path(layout_path) } else { // We look in the default dir - Layout::from_dir(layout_path, layout_dir.as_ref()) + Layout::stringified_from_dir(layout_path, layout_dir.as_ref()) } }, None => { - Layout::from_dir( + Layout::stringified_from_dir( &std::path::PathBuf::from("default"), layout_dir.as_ref(), ) } } } - pub fn from_dir( + pub fn from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option, config: Config) -> Result<(Layout, Config), ConfigError> { + let raw_layout = Layout::stringified_from_path_or_default(layout_path, layout_dir)?; + let kdl_layout: KdlDocument = raw_layout.parse()?; + let layout = Layout::from_kdl(&kdl_layout)?; + let config = Config::from_kdl(&raw_layout, Some(config))?; // this merges the two config, with + Ok((layout, config)) + } + pub fn stringified_from_dir( layout: &PathBuf, layout_dir: Option<&PathBuf>, - ) -> Result { + ) -> Result { match layout_dir { Some(dir) => { let layout_path = &dir.join(layout); if layout_path.with_extension("kdl").exists() { - Self::from_path(layout_path) + Self::stringified_from_path(layout_path) } else { - Layout::from_default_assets(layout) + Layout::stringified_from_default_assets(layout) } }, - None => Layout::from_default_assets(layout), + None => Layout::stringified_from_default_assets(layout), } } - pub fn from_path(layout_path: &Path) -> Result { + pub fn stringified_from_path(layout_path: &Path) -> Result { let mut layout_file = File::open(&layout_path) .or_else(|_| File::open(&layout_path.with_extension("kdl"))) .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; let mut kdl_layout = String::new(); layout_file.read_to_string(&mut kdl_layout)?; - let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout) - } - pub fn from_kdl(kdl_layout: &KdlDocument) -> Result { - KdlLayoutParser::new(&kdl_layout).parse() + Ok(kdl_layout) } - pub fn from_default_assets(path: &Path) -> Result { + pub fn stringified_from_default_assets(path: &Path) -> Result { // TODO: ideally these should not be hard-coded // we should load layouts by name from the config // and load them from a hashmap or some such match path.to_str() { - Some("default") => Self::default_from_assets(), - Some("strider") => Self::strider_from_assets(), - Some("disable-status-bar") => Self::disable_status_from_assets(), - Some("compact") => Self::compact_from_assets(), + Some("default") => Self::stringified_default_from_assets(), + Some("strider") => Self::stringified_strider_from_assets(), + Some("disable-status-bar") => Self::stringified_disable_status_from_assets(), + Some("compact") => Self::stringified_compact_from_assets(), None | Some(_) => Err(ConfigError::IoPath( std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), path.into(), )), } } - - pub fn default_from_assets() -> Result { - let kdl_layout = String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?; - let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout) + pub fn stringified_default_from_assets() -> Result { + Ok(String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?) } - pub fn strider_from_assets() -> Result { - let kdl_layout = String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?; - let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout) + pub fn stringified_strider_from_assets() -> Result { + Ok(String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?) } - pub fn disable_status_from_assets() -> Result { - let kdl_layout = String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?; - let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout) + pub fn stringified_disable_status_from_assets() -> Result { + Ok(String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?) } - pub fn compact_from_assets() -> Result { - let kdl_layout = String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?; - let kdl_layout: KdlDocument = kdl_layout.parse()?; - Layout::from_kdl(&kdl_layout) + pub fn stringified_compact_from_assets() -> Result { + Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?) } pub fn total_terminal_panes(&self) -> usize { @@ -380,12 +340,6 @@ impl Layout { split_space(space, self) } - pub fn merge_layout_parts(&mut self, mut parts: Vec) { - // TODO - unimplemented!() - // self.parts.append(&mut parts); - } - pub fn new_tab(&self) -> Layout { match &self.template { Some(template) => *template.clone(), diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 870791ae30..92750aed65 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -150,73 +150,6 @@ impl Options { Options::default() } } -// pub fn from_kdl(kdl_options: &KdlDocument) -> Self { -// let on_force_close = kdl_options.get("on_force_close") -// .and_then(|on_force_close| on_force_close.entries().iter().next()) -// .and_then(|on_force_close| on_force_close.value().as_string()) -// .and_then(|on_force_close| ::from_str(on_force_close).ok()); -// let simplified_ui = kdl_options.get("simplified_ui") -// .and_then(|simplified_ui| simplified_ui.entries().iter().next()) -// .and_then(|simplified_ui| simplified_ui.value().as_bool()); -// let default_shell = kdl_options.get("default_shell") -// .and_then(|default_shell| default_shell.entries().iter().next()) -// .and_then(|default_shell| default_shell.value().as_string()) -// .map(|default_shell| PathBuf::from(default_shell)); -// let pane_frames = kdl_options.get("pane_frames") -// .and_then(|pane_frames| pane_frames.entries().iter().next()) -// .and_then(|pane_frames| pane_frames.value().as_bool()); -// let theme = kdl_options.get("theme") -// .and_then(|theme| theme.entries().iter().next()) -// .and_then(|theme| theme.value().as_string()) -// .map(|theme| theme.to_string()); -// let default_mode = kdl_options.get("default_mode") -// .and_then(|default_mode| default_mode.entries().iter().next()) -// .and_then(|default_mode| default_mode.value().as_string()) -// .and_then(|default_mode| InputMode::try_from(default_mode).ok()); -// let mouse_mode = kdl_options.get("mouse_mode") -// .and_then(|mouse_mode| mouse_mode.entries().iter().next()) -// .and_then(|mouse_mode| mouse_mode.value().as_bool()); -// let scroll_buffer_size = kdl_options.get("scroll_buffer_size") -// .and_then(|scroll_buffer_size| scroll_buffer_size.entries().iter().next()) -// .and_then(|scroll_buffer_size| scroll_buffer_size.value().as_i64()) -// .map(|scroll_buffer_size| scroll_buffer_size as usize); -// let copy_command = kdl_options.get("copy_command") -// .and_then(|copy_command| copy_command.entries().iter().next()) -// .and_then(|copy_command| copy_command.value().as_string()) -// .map(|copy_command| copy_command.to_string()); -// let copy_clipboard = kdl_options.get("copy_clipboard") -// .and_then(|copy_clipboard| copy_clipboard.entries().iter().next()) -// .and_then(|copy_clipboard| copy_clipboard.value().as_string()) -// .and_then(|on_force_close| ::from_str(on_force_close).ok()); -// let copy_on_select = kdl_options.get("copy_on_select") -// .and_then(|copy_on_select| copy_on_select.entries().iter().next()) -// .and_then(|copy_on_select| copy_on_select.value().as_bool()); -// let scrollback_editor = kdl_options.get("scrollback_editor") -// .and_then(|scrollback_editor| scrollback_editor.entries().iter().next()) -// .and_then(|scrollback_editor| scrollback_editor.value().as_string()) -// .map(|scrollback_editor| PathBuf::from(scrollback_editor)); -// let mirror_session = kdl_options.get("mirror_session") -// .and_then(|mirror_session| mirror_session.entries().iter().next()) -// .and_then(|mirror_session| mirror_session.value().as_bool()); -// Options { -// simplified_ui, -// theme, -// default_mode, -// default_shell, -// default_layout: Some(PathBuf::from("default")), // TODO -// layout_dir: None, // TODO -// mouse_mode, -// pane_frames, -// mirror_session, -// on_force_close, -// scroll_buffer_size, -// copy_command, -// copy_clipboard, -// copy_on_select, -// scrollback_editor, -// } -// } - /// Merges two [`Options`] structs, a `Some` in `other` /// will supersede a `Some` in `self` // TODO: Maybe a good candidate for a macro? @@ -351,7 +284,7 @@ pub struct CliOptions { #[clap(long, conflicts_with("pane-frames"), value_parser)] pub no_pane_frames: bool, #[clap(flatten)] - options: Options, + pub options: Options, } impl From for Options { diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index 64ff12c3cc..83ab967f86 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -18,6 +18,9 @@ use super::layout::{RunPlugin, RunPluginLocation}; pub use crate::data::PluginTag; use crate::setup; +use std::fmt; +use std::collections::BTreeMap; + // lazy_static! { // static ref DEFAULT_CONFIG_PLUGINS: PluginsConfig = { // let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec()).unwrap(); @@ -30,8 +33,18 @@ use crate::setup; // pub struct PluginsConfigFromYaml(Vec); /// Used in the config struct for plugin metadata -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct PluginsConfig(HashMap); +#[derive(Clone, PartialEq, Deserialize, Serialize)] +pub struct PluginsConfig(pub HashMap); + +impl fmt::Debug for PluginsConfig { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut stable_sorted = BTreeMap::new(); + for (plugin_tag, plugin_config) in self.0.iter() { + stable_sorted.insert(plugin_tag, plugin_config); + } + write!(f, "{:#?}", stable_sorted) + } +} impl PluginsConfig { pub fn new() -> Self { @@ -40,33 +53,6 @@ impl PluginsConfig { pub fn from_data(data: HashMap) -> Self { PluginsConfig(data) } - pub fn from_kdl(kdl_plugin_config: &KdlNode) -> Result { - let mut plugins: HashMap = HashMap::new(); - for plugin_config in kdl_children_nodes_or_error!(kdl_plugin_config, "no plugin config found") { - let plugin_name = kdl_name!(plugin_config); - let plugin_tag = PluginTag::new(plugin_name); - let path = kdl_children_property_first_arg_as_string!(plugin_config, "path") - .map(|path| PathBuf::from(path)) - .ok_or::>("Plugin path not found".into())?; - let allow_exec_host_cmd = kdl_children_property_first_arg_as_bool!(plugin_config, "_allow_exec_host_cmd") - .unwrap_or(false); - let plugin_config = PluginConfig { - path, - run: PluginType::Pane(None), - location: RunPluginLocation::Zellij(plugin_tag.clone()), - _allow_exec_host_cmd: allow_exec_host_cmd, - }; - plugins.insert(plugin_tag, plugin_config); - } - Ok(PluginsConfig(plugins)) - } - - /// Entrypoint from the config module -// pub fn get_plugins_with_default(user_plugins: Self) -> Self { -// let mut base_plugins = DEFAULT_CONFIG_PLUGINS.clone(); -// base_plugins.0.extend(user_plugins.0); -// base_plugins -// } /// Get plugin config from run configuration specified in layout files. pub fn get(&self, run: impl Borrow) -> Option { diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index e4de311fd2..f8f27843ca 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -2,7 +2,7 @@ use serde::{ de::{Error, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; -use std::{collections::HashMap, fmt}; +use std::{collections::{HashMap, BTreeMap}, fmt}; use kdl::KdlNode; use crate::{entry_count, kdl_entries_as_i64, kdl_first_entry_as_string, kdl_first_entry_as_i64, kdl_children_or_error, kdl_name, kdl_children_nodes_or_error, kdl_get_child, kdl_children_property_first_arg_as_bool}; @@ -22,14 +22,10 @@ pub struct UiConfig { } impl UiConfig { - pub fn from_kdl(kdl_ui_config: &KdlNode) -> Result { - let mut ui_config = UiConfig::default(); - if let Some(pane_frames) = kdl_get_child!(kdl_ui_config, "pane_frames") { - let rounded_corners = kdl_children_property_first_arg_as_bool!(pane_frames, "rounded_corners").unwrap_or(false); - let frame_config = FrameConfig { rounded_corners }; - ui_config.pane_frames = frame_config; - } - Ok(ui_config) + pub fn merge(&self, other: UiConfig) -> Self { + let mut merged = self.clone(); + merged.pane_frames = merged.pane_frames.merge(other.pane_frames); + merged } } @@ -38,38 +34,37 @@ pub struct FrameConfig { pub rounded_corners: bool, } -#[derive(Debug, Clone, PartialEq, Default)] +impl FrameConfig { + pub fn merge(&self, other: FrameConfig) -> Self { + let mut merged = self.clone(); + merged.rounded_corners = other.rounded_corners; + merged + } +} + +#[derive(Clone, PartialEq, Default)] pub struct Themes(HashMap); +impl fmt::Debug for Themes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut stable_sorted = BTreeMap::new(); + for (theme_name, theme) in self.0.iter() { + stable_sorted.insert(theme_name, theme); + } + write!(f, "{:#?}", stable_sorted) + } +} + impl Themes { pub fn from_data(theme_data: HashMap) -> Self { Themes(theme_data) } - pub fn from_kdl(themes_from_kdl: &KdlNode) -> Result { - let mut themes: HashMap = HashMap::new(); - for theme_config in kdl_children_nodes_or_error!(themes_from_kdl, "no themes found") { - let theme_name = kdl_name!(theme_config); - let theme_colors = kdl_children_or_error!(theme_config, "empty theme"); - let theme = Theme { - palette: Palette { - fg: PaletteColor::try_from(("fg", theme_colors))?, - bg: PaletteColor::try_from(("bg", theme_colors))?, - red: PaletteColor::try_from(("red", theme_colors))?, - green: PaletteColor::try_from(("green", theme_colors))?, - yellow: PaletteColor::try_from(("yellow", theme_colors))?, - blue: PaletteColor::try_from(("blue", theme_colors))?, - magenta: PaletteColor::try_from(("magenta", theme_colors))?, - orange: PaletteColor::try_from(("orange", theme_colors))?, - cyan: PaletteColor::try_from(("cyan", theme_colors))?, - black: PaletteColor::try_from(("black", theme_colors))?, - white: PaletteColor::try_from(("white", theme_colors))?, - ..Default::default() - } - }; - themes.insert(theme_name.into(), theme); + pub fn merge(&self, mut other: Themes) -> Self { + let mut merged = self.clone(); + for (name, theme) in other.0.drain() { + merged.0.insert(name, theme); } - let themes = Themes::from_data(themes); - Ok(themes) + merged } pub fn get_theme(&self, theme_name: &str) -> Option<&Theme> { self.0.get(theme_name) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap index 0492ad9cb2..b40692ca35 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap @@ -13,8 +13,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: Some( @@ -43,8 +41,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -64,8 +60,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -80,8 +74,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -92,8 +84,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -104,8 +94,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -120,8 +108,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -132,8 +118,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -158,8 +142,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -170,8 +152,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: Some( 1, ), @@ -188,8 +168,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -200,8 +178,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -212,8 +188,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap index 555f17850c..9c83214cdf 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap @@ -32,8 +32,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -53,8 +51,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -69,8 +65,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -81,8 +75,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -93,8 +85,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -109,8 +99,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -121,8 +109,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -150,8 +136,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -162,8 +146,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: Some( 1, ), @@ -180,8 +162,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -192,8 +172,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -205,8 +183,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: Some( @@ -225,8 +201,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -237,8 +211,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap index 25fc00846d..9e16111f2d 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap @@ -32,8 +32,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -55,8 +53,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -67,8 +63,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -83,8 +77,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -95,8 +87,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -116,8 +106,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -132,8 +120,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -144,8 +130,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -156,8 +140,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -185,8 +167,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -208,8 +188,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -220,8 +198,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -236,8 +212,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -248,8 +222,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -264,8 +236,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -276,8 +246,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -289,8 +257,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: Some( @@ -309,8 +275,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -321,8 +285,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap index 9cd6f2f73e..da5b11eff2 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 760 +assertion_line: 761 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -13,8 +13,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: Some( @@ -38,8 +36,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -54,8 +50,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -75,8 +69,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -91,8 +83,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -103,8 +93,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -124,8 +112,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -140,8 +126,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -156,8 +140,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -168,8 +150,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -180,8 +160,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -192,8 +170,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap index b88cf0bc36..5e7d79d323 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 733 +assertion_line: 734 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -13,8 +13,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: Some( @@ -38,8 +36,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -54,8 +50,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -75,8 +69,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -91,8 +83,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -103,8 +93,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -119,8 +107,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -131,8 +117,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -143,8 +127,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap index d098d82de0..5f958f4f52 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 711 +assertion_line: 712 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -30,8 +30,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -59,8 +57,6 @@ Layout { ), borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -71,8 +67,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -87,8 +81,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -99,8 +91,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -111,8 +101,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -124,8 +112,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: Some( @@ -144,8 +130,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, @@ -156,8 +140,6 @@ Layout { run: None, borderless: false, focus: None, - session_name: None, - attach_to_session: false, external_children_index: None, focused_tab_index: None, template: None, diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index a7e04928d0..7c55dc2c1e 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,15 +1,25 @@ - -pub mod kdl_layout_parser; -// use super::layout::TabLayout; -use crate::data::{InputMode, Key, CharOrArrow, PaletteColor}; +mod kdl_layout_parser; +use kdl_layout_parser::KdlLayoutParser; +use crate::envs::EnvironmentVariables; +use crate::input::command::RunCommand; +use crate::input::keybinds::Keybinds; +use crate::input::layout::{Layout, RunPlugin, RunPluginLocation}; +use crate::input::config::{Config, ConfigError}; +use url::Url; +use crate::data::{InputMode, Key, CharOrArrow, PaletteColor, Palette}; use crate::input::options::{Options, OnForceClose, Clipboard}; +use std::collections::HashMap; +use crate::input::plugins::{PluginsConfig, PluginsConfigError, PluginConfig, PluginType, PluginTag}; +use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; +use crate::cli::{CliArgs, Command}; +use crate::setup; use kdl::{KdlDocument, KdlValue, KdlNode}; use std::str::FromStr; use std::path::PathBuf; -use crate::input::actions::{Action, ResizeDirection, Direction}; +use crate::input::actions::{Action, ResizeDirection, Direction, SearchOption, SearchDirection}; use crate::input::command::RunCommandAction; #[macro_export] @@ -246,7 +256,7 @@ impl Action { match action_name { "WriteChars" => Ok(Action::WriteChars(string)), "SwitchToMode" => { - match InputMode::try_from(string.as_str()) { + match InputMode::from_str(string.as_str()) { Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)), Err(_e) => return Err(format!("Failed to parse SwitchToMode. Unknown InputMode: {}", string).into()), } @@ -282,6 +292,14 @@ impl Action { Ok(Action::NewPane(Some(direction))) } } + "SearchToggleOption" => { + let toggle_option = SearchOption::from_str(string.as_str())?; + Ok(Action::SearchToggleOption(toggle_option)) + } + "Search" => { + let search_direction = SearchDirection::from_str(string.as_str())?; + Ok(Action::Search(search_direction)) + } _ => Err(format!("Cannot create action: '{}' from string: '{:?}'", action_name, string).into()), } } @@ -344,10 +362,8 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor { } } -// impl TryFrom<(&str, Vec<&KdlValue>, Vec<&KdlDocument>)> for Action { impl TryFrom<&KdlNode> for Action { type Error = Box; - // fn try_from((action_name, action_arguments, action_children): (&str, Vec<&KdlValue>, Vec<&KdlDocument>)) -> Result { fn try_from(kdl_action: &KdlNode) -> Result { let action_name = kdl_name!(kdl_action); @@ -386,6 +402,7 @@ impl TryFrom<&KdlNode> for Action { "Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "WriteChars" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "SwitchToMode" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "Search" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "Resize" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "MoveFocus" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "MoveFocusOrTab" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), @@ -396,6 +413,7 @@ impl TryFrom<&KdlNode> for Action { "NewTab" => Ok(Action::NewTab(None, None)), // TODO: consider the Some(TabLayout, "tab_name") case... "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "SearchToggleOption" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "Run" => { let arguments = action_arguments.iter().copied(); let mut args = kdl_arguments_that_are_strings(arguments)?; @@ -669,7 +687,7 @@ impl Options { let theme = kdl_property_first_arg_as_string!(kdl_options, "theme") .map(|theme| theme.to_string()); let default_mode = kdl_property_first_arg_as_string!(kdl_options, "default_mode") - .and_then(|default_mode| InputMode::try_from(default_mode).ok()); + .and_then(|default_mode| InputMode::from_str(default_mode).ok()); let default_layout = kdl_property_first_arg_as_string!(kdl_options, "default_layout") .map(|default_layout| PathBuf::from(default_layout)); let layout_dir = kdl_property_first_arg_as_string!(kdl_options, "layout_dir") @@ -711,3 +729,208 @@ impl Options { } } } + +impl RunPlugin { + pub fn from_kdl(kdl_node: &KdlNode) -> Result { + let _allow_exec_host_cmd = kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false); + let string_url = kdl_get_child_entry_string_value!(kdl_node, "location").ok_or(ConfigError::KdlParsingError("Plugins must have a location".into()))?; + let url = Url::parse(string_url).map_err(|e| ConfigError::KdlParsingError(format!("Failed to aprse url: {:?}", e)))?; + let location = RunPluginLocation::try_from(url)?; + Ok(RunPlugin { + _allow_exec_host_cmd, + location, + }) + } +} +impl Layout { + pub fn from_kdl(kdl_layout: &KdlDocument) -> Result { + KdlLayoutParser::new(&kdl_layout).parse() + } +} +impl EnvironmentVariables { + pub fn from_kdl(kdl_env_variables: &KdlNode) -> Result { + let mut env: HashMap = HashMap::new(); + for env_var in kdl_children_nodes_or_error!(kdl_env_variables, "empty env variable block") { + let env_var_name = kdl_name!(env_var); + let env_var_str_value = kdl_first_entry_as_string!(env_var).map(|s| format!("{}", s.to_string())); + let env_var_int_value = kdl_first_entry_as_i64!(env_var).map(|s| format!("{}", s.to_string())); + let env_var_value = env_var_str_value + .or(env_var_int_value) + .ok_or::>(format!("Failed to parse env var: {:?}", env_var_name).into())?; + env.insert(env_var_name.into(), env_var_value); + } + Ok(EnvironmentVariables::from_data(env)) + } +} + +impl Keybinds { + pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result { + let clear_defaults = kdl_arg_is_truthy!(kdl_keybinds, "clear-defaults"); + let mut keybinds_from_config = if clear_defaults { Keybinds::default() } else { base_keybinds }; + for mode in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { + if kdl_name!(mode) == "unbind" { + continue; + } + let mut input_mode_keybinds = Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?; + let bind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); + let unbind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); + for key_block in bind_nodes { + Keybinds::bind_actions_for_each_key(key_block, &mut input_mode_keybinds)?; + } + // we loop twice so that the unbinds always happen after the binds + for key_block in unbind_nodes { + Keybinds::unbind_keys(key_block, &mut input_mode_keybinds)?; + } + } + if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { + Keybinds::unbind_keys_in_all_modes(global_unbind, &mut keybinds_from_config)?; + }; + Ok(keybinds_from_config) + } + fn bind_actions_for_each_key(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ + let keys: Vec = keys_from_kdl!(key_block); + let actions: Vec = actions_from_kdl!(key_block); + for key in keys { + input_mode_keybinds.insert(key, actions.clone()); + } + Ok(()) + } + fn unbind_keys(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ + let keys: Vec = keys_from_kdl!(key_block); + for key in keys { + input_mode_keybinds.remove(&key); + } + Ok(()) + } + fn unbind_keys_in_all_modes(global_unbind: &KdlNode, keybinds_from_config: &mut Keybinds) -> Result<(), ConfigError> { + let keys: Vec = keys_from_kdl!(global_unbind); + for mode in keybinds_from_config.0.values_mut() { + for key in &keys { + mode.remove(&key); + } + } + Ok(()) + } + fn input_mode_keybindings <'a>(mode: &KdlNode, keybinds_from_config: &'a mut Keybinds) -> Result<&'a mut HashMap>, ConfigError> { + let mode_name = kdl_name!(mode); + let input_mode = InputMode::from_str(mode_name)?; + let input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&input_mode); + let clear_defaults_for_mode = kdl_arg_is_truthy!(mode, "clear-defaults"); + if clear_defaults_for_mode { + input_mode_keybinds.clear(); + } + Ok(input_mode_keybinds) + } +} + +impl RunCommand { + pub fn from_kdl(kdl_node: &KdlNode) -> Result { + let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or(ConfigError::KdlParsingError("Command must have a cmd value".into()))?); + let cwd = kdl_get_child_entry_string_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); + let args = match kdl_get_child!(kdl_node, "args") { + Some(kdl_args) => { + kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect() + }, + None => vec![] + }; + Ok(RunCommand { + command, + args, + cwd, + }) + } +} + +impl Config { + pub fn from_kdl(kdl_config: &str, base_config: Option) -> Result { + let mut config = base_config.unwrap_or_else(|| Config::default()); + let kdl_config: KdlDocument = kdl_config.parse()?; + // TODO: handle cases where we have more than one of these blocks (eg. two "keybinds") + // this should give an informative parsing error + if let Some(kdl_keybinds) = kdl_config.get("keybinds") { + config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds)?; + } + let config_options = Options::from_kdl(&kdl_config); + config.options = config.options.merge(config_options); + if let Some(kdl_themes) = kdl_config.get("themes") { + let config_themes = Themes::from_kdl(kdl_themes)?; + config.themes = config.themes.merge(config_themes); + } + if let Some(kdl_plugin_config) = kdl_config.get("plugins") { + let config_plugins = PluginsConfig::from_kdl(kdl_plugin_config)?; + config.plugins = config.plugins.merge(config_plugins); + } + if let Some(kdl_ui_config) = kdl_config.get("ui") { + let config_ui = UiConfig::from_kdl(&kdl_ui_config)?; + config.ui = config.ui.merge(config_ui); + } + if let Some(env_config) = kdl_config.get("env") { + let config_env = EnvironmentVariables::from_kdl(&env_config)?; + config.env = config.env.merge(config_env); + } + Ok(config) + } +} + +impl PluginsConfig { + pub fn from_kdl(kdl_plugin_config: &KdlNode) -> Result { + let mut plugins: HashMap = HashMap::new(); + for plugin_config in kdl_children_nodes_or_error!(kdl_plugin_config, "no plugin config found") { + let plugin_name = kdl_name!(plugin_config); + let plugin_tag = PluginTag::new(plugin_name); + let path = kdl_children_property_first_arg_as_string!(plugin_config, "path") + .map(|path| PathBuf::from(path)) + .ok_or::>("Plugin path not found".into())?; + let allow_exec_host_cmd = kdl_children_property_first_arg_as_bool!(plugin_config, "_allow_exec_host_cmd") + .unwrap_or(false); + let plugin_config = PluginConfig { + path, + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(plugin_tag.clone()), + _allow_exec_host_cmd: allow_exec_host_cmd, + }; + plugins.insert(plugin_tag, plugin_config); + } + Ok(PluginsConfig(plugins)) + } +} +impl UiConfig { + pub fn from_kdl(kdl_ui_config: &KdlNode) -> Result { + let mut ui_config = UiConfig::default(); + if let Some(pane_frames) = kdl_get_child!(kdl_ui_config, "pane_frames") { + let rounded_corners = kdl_children_property_first_arg_as_bool!(pane_frames, "rounded_corners").unwrap_or(false); + let frame_config = FrameConfig { rounded_corners }; + ui_config.pane_frames = frame_config; + } + Ok(ui_config) + } +} + +impl Themes { + pub fn from_kdl(themes_from_kdl: &KdlNode) -> Result { + let mut themes: HashMap = HashMap::new(); + for theme_config in kdl_children_nodes_or_error!(themes_from_kdl, "no themes found") { + let theme_name = kdl_name!(theme_config); + let theme_colors = kdl_children_or_error!(theme_config, "empty theme"); + let theme = Theme { + palette: Palette { + fg: PaletteColor::try_from(("fg", theme_colors))?, + bg: PaletteColor::try_from(("bg", theme_colors))?, + red: PaletteColor::try_from(("red", theme_colors))?, + green: PaletteColor::try_from(("green", theme_colors))?, + yellow: PaletteColor::try_from(("yellow", theme_colors))?, + blue: PaletteColor::try_from(("blue", theme_colors))?, + magenta: PaletteColor::try_from(("magenta", theme_colors))?, + orange: PaletteColor::try_from(("orange", theme_colors))?, + cyan: PaletteColor::try_from(("cyan", theme_colors))?, + black: PaletteColor::try_from(("black", theme_colors))?, + white: PaletteColor::try_from(("white", theme_colors))?, + ..Default::default() + } + }; + themes.insert(theme_name.into(), theme); + } + let themes = Themes::from_data(themes); + Ok(themes) + } +} diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index aa5fd364ec..ecc45b61a8 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -194,64 +194,30 @@ impl Setup { /// 2. layout options /// (`layout.yaml` / `zellij --layout`) /// 3. config options (`config.yaml`) - pub fn from_options( - opts: &CliArgs, - // ) -> Result<(Config, Option, Options), ConfigError> { + pub fn from_cli_args( + cli_args: &CliArgs, ) -> Result<(Config, Layout, Options), ConfigError> { - let clean = match &opts.command { - Some(Command::Setup(ref setup)) => setup.clean, - _ => false, - }; - - // setup functions that don't require deserialisation of the config - if let Some(Command::Setup(ref setup)) = &opts.command { - setup.from_cli().map_or_else( - |e| { - eprintln!("{:?}", e); - process::exit(1); - }, - |_| {}, - ); + let clean = cli_args.should_clean_config(); + // note that this can potentially exit the process + Setup::handle_setup_commands(cli_args); + let config = if clean { + Config::default() + } else { + Config::try_from(cli_args)? }; - - let config = if !clean { - match Config::try_from(opts) { - Ok(config) => config, - Err(e) => { - return Err(e); - }, - } + let cli_config_options: Option = if let Some(Command::Options(options)) = cli_args.command.clone() { + Some(options.into()) } else { - Config::default() + None }; - - let config_options = Options::from_cli(&config.options, opts.command.clone()); - - let layout_dir = config_options - .layout_dir - .clone() - .or_else(|| get_layout_dir(opts.config_dir.clone().or_else(find_default_config_dir))); - let chosen_layout = opts - .layout - .clone() - .or_else(|| config_options.default_layout.clone()); - -// // TODO: CONTINUE HERE - change this to Layout::from_kdl and have this function return a -// // Layout instead of a LayoutFromYaml -// let layout_result = -// LayoutFromYamlIntermediate::from_path_or_default(chosen_layout.as_ref(), layout_dir); -// let layout = match layout_result { -// None => None, -// Some(Ok(layout)) => Some(layout), -// Some(Err(e)) => { -// return Err(e); -// }, -// }; - let layout = Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir)?; - - if let Some(Command::Setup(ref setup)) = &opts.command { + let (layout, config) = Setup::parse_layout_and_override_config(cli_config_options.as_ref(), config, cli_args)?; + let config_options = match cli_config_options { + Some(cli_config_options) => config.options.merge(cli_config_options), + None => config.options.clone() + }; + if let Some(Command::Setup(ref setup)) = &cli_args.command { setup - .from_cli_with_options(opts, &config_options) + .from_cli_with_options(cli_args, &config_options) .map_or_else( |e| { eprintln!("{:?}", e); @@ -260,9 +226,7 @@ impl Setup { |_| {}, ); }; - - Ok((config, layout, config_options)) // TODO: no!!!!!111oneoneone - // Setup::merge_config_with_layout(config, layout, config_options) + Ok((config, layout, config_options)) } /// General setup helpers @@ -307,33 +271,6 @@ impl Setup { Ok(()) } - fn merge_config_with_layout( - config: Config, - // layout: Option, - layout: Layout, - config_options: Options, - // ) -> Result<(Config, Option, Options), ConfigError> { - ) -> Result<(Config, Layout, Options), ConfigError> { - unimplemented!() -// let (layout, layout_config) = match layout.map(|l| l.to_layout_and_config()) { -// None => (None, None), -// Some((layout, layout_config)) => (Some(layout), layout_config), -// }; -// -// let (config, config_options) = if let Some(layout_config) = layout_config { -// let config_options = if let Some(options) = layout_config.options.clone() { -// config_options.merge(options) -// } else { -// config_options -// }; -// // let config = config.merge(layout_config.try_into()?); // TODO: NO! handle this! -// (config, config_options) -// } else { -// (config, config_options) -// }; -// Ok((config, layout, config_options)) - } - pub fn check_defaults_config(opts: &CliArgs, config_options: &Options) -> std::io::Result<()> { let data_dir = opts.data_dir.clone().unwrap_or_else(get_default_data_dir); let config_dir = opts.config_dir.clone().or_else(find_default_config_dir); @@ -481,103 +418,134 @@ impl Setup { _ => {}, } } + fn parse_layout_and_override_config(cli_config_options: Option<&Options>, config: Config, cli_args: &CliArgs) -> Result<(Layout, Config), ConfigError> { + // find the layout folder relative to which we'll look for our layout + let layout_dir = cli_config_options.as_ref() + .and_then(|cli_options| cli_options.layout_dir.clone()) + .or_else(|| config.options.layout_dir.clone()) + .or_else(|| get_layout_dir(cli_args.config_dir.clone().or_else(find_default_config_dir))); + // the chosen layout can either be a path relative to the layout_dir or a name of one + // of our assets, this distinction is made when parsing the layout - TODO: ideally, this + // logic should not be split up and all the decisions should happen here + let chosen_layout = cli_args + .layout + .clone() + .or_else(|| cli_config_options.as_ref().and_then(|cli_options| cli_options.default_layout.clone())); + // we merge-override the config here because the layout might contain configuration + // that needs to take precedence + let (layout, config) = Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir.clone(), config)?; + Ok((layout, config)) + } + fn handle_setup_commands(cli_args: &CliArgs) { + if let Some(Command::Setup(ref setup)) = &cli_args.command { + setup.from_cli().map_or_else( + |e| { + eprintln!("{:?}", e); + process::exit(1); + }, + |_| {}, + ); + }; + } + } -// TODO: write these test cases once we're done with the layout -// -// #[cfg(test)] -// mod setup_test { -// use super::Setup; -// use crate::data::InputMode; -// use crate::input::{ -// config::{Config, ConfigError}, -// layout::LayoutFromYamlIntermediate, -// options::Options, -// }; -// -// fn deserialise_config_and_layout( -// config: &str, -// layout: &str, -// ) -> Result<(Config, LayoutFromYamlIntermediate), ConfigError> { -// // let config = Config::from_yaml(config)?; -// let config = Config::from_kdl(config)?; -// let layout = LayoutFromYamlIntermediate::from_yaml(layout)?; -// Ok((config, layout)) -// } -// -// #[test] -// fn empty_config_empty_layout() { -// let goal = Config::default(); -// let config = r""; -// let layout = r""; -// let config_layout_result = deserialise_config_and_layout(config, layout); -// let (config, layout) = config_layout_result.unwrap(); -// let config_options = Options::default(); -// let (config, _layout, _config_options) = -// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); -// assert_eq!(config, goal); -// } -// -// #[test] -// fn config_empty_layout() { -// let mut goal = Config::default(); -// goal.options.default_shell = Some(std::path::PathBuf::from("fish")); -// let config = r"--- -// default_shell: fish"; -// let layout = r""; -// let config_layout_result = deserialise_config_and_layout(config, layout); -// let (config, layout) = config_layout_result.unwrap(); -// let config_options = Options::default(); -// let (config, _layout, _config_options) = -// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); -// assert_eq!(config, goal); -// } -// -// #[test] -// fn layout_overwrites_config() { -// let mut goal = Config::default(); -// goal.options.default_shell = Some(std::path::PathBuf::from("bash")); -// let config = r"--- -// default_shell: fish"; -// let layout = r"--- -// default_shell: bash"; -// let config_layout_result = deserialise_config_and_layout(config, layout); -// let (config, layout) = config_layout_result.unwrap(); -// let config_options = Options::default(); -// let (config, _layout, _config_options) = -// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); -// assert_eq!(config, goal); -// } -// -// #[test] -// fn empty_config_nonempty_layout() { -// let mut goal = Config::default(); -// goal.options.default_shell = Some(std::path::PathBuf::from("bash")); -// let config = r""; -// let layout = r"--- -// default_shell: bash"; -// let config_layout_result = deserialise_config_and_layout(config, layout); -// let (config, layout) = config_layout_result.unwrap(); -// let config_options = Options::default(); -// let (config, _layout, _config_options) = -// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); -// assert_eq!(config, goal); -// } -// -// #[test] -// fn nonempty_config_nonempty_layout() { -// let mut goal = Config::default(); -// goal.options.default_shell = Some(std::path::PathBuf::from("bash")); -// goal.options.default_mode = Some(InputMode::Locked); -// let config = r"--- -// default_mode: locked"; -// let layout = r"--- -// default_shell: bash"; -// let config_layout_result = deserialise_config_and_layout(config, layout); -// let (config, layout) = config_layout_result.unwrap(); -// let config_options = Options::default(); -// let (config, _layout, _config_options) = -// Setup::merge_config_with_layout(config, Some(layout), config_options).unwrap(); -// assert_eq!(config, goal); -// } -// } +#[cfg(test)] +mod setup_test { + use super::Setup; + use std::path::PathBuf; + use insta::assert_snapshot; + use crate::cli::{CliArgs, Command}; + use crate::input::{ + options::{Options, CliOptions}, + }; + + #[test] + fn default_config_with_no_cli_arguments() { + let cli_args = CliArgs::default(); + let (config, layout, options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", config)); + assert_snapshot!(format!("{:#?}", layout)); + assert_snapshot!(format!("{:#?}", options)); + } + #[test] + fn cli_arguments_override_config_options() { + let mut cli_args = CliArgs::default(); + cli_args.command = Some(Command::Options( + CliOptions { + options: Options { + simplified_ui: Some(true), + ..Default::default() + }, + ..Default::default() + } + )); + let (_config, _layout, options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", options)); + } + #[test] + fn layout_options_override_config_options() { + let mut cli_args = CliArgs::default(); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-options.kdl", env!("CARGO_MANIFEST_DIR")))); + let (_config, layout, options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", options)); + assert_snapshot!(format!("{:#?}", layout)); + } + #[test] + fn cli_arguments_override_layout_options() { + let mut cli_args = CliArgs::default(); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-options.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.command = Some(Command::Options( + CliOptions { + options: Options { + pane_frames: Some(true), + ..Default::default() + }, + ..Default::default() + } + )); + let (_config, layout, options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", options)); + assert_snapshot!(format!("{:#?}", layout)); + } + #[test] + fn layout_env_vars_override_config_env_vars() { + let mut cli_args = CliArgs::default(); + cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-env-vars.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-env-vars.kdl", env!("CARGO_MANIFEST_DIR")))); + let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", config)); + } + #[test] + fn layout_ui_config_overrides_config_ui_config() { + let mut cli_args = CliArgs::default(); + cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-ui-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-ui-config.kdl", env!("CARGO_MANIFEST_DIR")))); + let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", config)); + } + #[test] + fn layout_plugins_override_config_plugins() { + let mut cli_args = CliArgs::default(); + cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-plugins-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-plugins-config.kdl", env!("CARGO_MANIFEST_DIR")))); + let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", config)); + } + #[test] + fn layout_themes_override_config_themes() { + let mut cli_args = CliArgs::default(); + cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-themes-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-themes-config.kdl", env!("CARGO_MANIFEST_DIR")))); + let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", config)); + } + #[test] + fn layout_keybinds_override_config_keybinds() { + let mut cli_args = CliArgs::default(); + cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-keybindings-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-keybindings-config.kdl", env!("CARGO_MANIFEST_DIR")))); + let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); + assert_snapshot!(format!("{:#?}", config)); + } +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap new file mode 100644 index 0000000000..0080140332 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap @@ -0,0 +1,27 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 561 +expression: "format!(\"{:#?}\", options)" +--- +Options { + simplified_ui: Some( + true, + ), + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap new file mode 100644 index 0000000000..7e32b875ba --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap @@ -0,0 +1,49 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 509 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap new file mode 100644 index 0000000000..a36a7543f5 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap @@ -0,0 +1,27 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 584 +expression: "format!(\"{:#?}\", options)" +--- +Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: Some( + true, + ), + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap new file mode 100644 index 0000000000..3a5328593c --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap @@ -0,0 +1,107 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 468 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap new file mode 100644 index 0000000000..e23c0d246e --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap @@ -0,0 +1,25 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 546 +expression: "format!(\"{:#?}\", options)" +--- +Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap new file mode 100644 index 0000000000..52b0b0f6d4 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -0,0 +1,2945 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 543 +expression: "format!(\"{:#?}\", config)" +--- +Config { + keybinds: { + Normal: { + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Char( + 'y', + ), + ): [ + Run( + RunCommandAction { + command: "diskonaut", + args: [], + cwd: Some( + "/home/aram", + ), + direction: None, + }, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + }, + Locked: { + Ctrl( + 'g', + ): [ + SwitchToMode( + Normal, + ), + ], + }, + Resize: { + Left: [ + Resize( + Left, + ), + ], + Down: [ + Resize( + Down, + ), + ], + Up: [ + Resize( + Up, + ), + ], + Right: [ + Resize( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '+', + ): [ + Resize( + Increase, + ), + ], + Char( + '-', + ): [ + Resize( + Decrease, + ), + ], + Char( + '=', + ): [ + Resize( + Increase, + ), + ], + Char( + 'h', + ): [ + Resize( + Left, + ), + ], + Char( + 'j', + ): [ + Resize( + Down, + ), + ], + Char( + 'k', + ): [ + Resize( + Up, + ), + ], + Char( + 'l', + ): [ + Resize( + Right, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Pane: { + Left: [ + MoveFocus( + Left, + ), + ], + Down: [ + MoveFocus( + Down, + ), + ], + Up: [ + MoveFocus( + Up, + ), + ], + Right: [ + MoveFocus( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SwitchToMode( + RenamePane, + ), + ], + Char( + 'd', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'e', + ): [ + TogglePaneEmbedOrFloating, + SwitchToMode( + Normal, + ), + ], + Char( + 'f', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + ], + Char( + 'n', + ): [ + NewPane( + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'p', + ): [ + SwitchFocus, + ], + Char( + 'r', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'w', + ): [ + ToggleFloatingPanes, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseFocus, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + TogglePaneFrames, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tab: { + Left: [ + GoToPreviousTab, + ], + Down: [ + GoToNextTab, + ], + Up: [ + GoToPreviousTab, + ], + Right: [ + GoToNextTab, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '1', + ): [ + GoToTab( + 1, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '2', + ): [ + GoToTab( + 2, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '3', + ): [ + GoToTab( + 3, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '4', + ): [ + GoToTab( + 4, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '5', + ): [ + GoToTab( + 5, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '6', + ): [ + GoToTab( + 6, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '7', + ): [ + GoToTab( + 7, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '8', + ): [ + GoToTab( + 8, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '9', + ): [ + GoToTab( + 9, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + GoToPreviousTab, + ], + Char( + 'j', + ): [ + GoToNextTab, + ], + Char( + 'k', + ): [ + GoToPreviousTab, + ], + Char( + 'l', + ): [ + GoToNextTab, + ], + Char( + 'n', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'r', + ): [ + SwitchToMode( + RenameTab, + ), + TabNameInput( + [ + 0, + ], + ), + ], + Char( + 's', + ): [ + ToggleActiveSyncTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseTab, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Normal, + ), + ], + BackTab: [ + ToggleTab, + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Scroll: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'e', + ): [ + EditScrollback, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 's', + ): [ + SwitchToMode( + EnterSearch, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + EnterSearch: { + Char( + '\n', + ): [ + SwitchToMode( + Search, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Scroll, + ), + ], + Esc: [ + SwitchToMode( + Scroll, + ), + ], + }, + Search: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SearchToggleOption( + CaseSensitivity, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 'n', + ): [ + Search( + Down, + ), + ], + Char( + 'o', + ): [ + SearchToggleOption( + WholeWord, + ), + ], + Char( + 'p', + ): [ + Search( + Up, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Char( + 'w', + ): [ + SearchToggleOption( + Wrap, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + RenameTab: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenameTab, + SwitchToMode( + Tab, + ), + ], + }, + RenamePane: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenamePane, + SwitchToMode( + Pane, + ), + ], + }, + Session: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Move: { + Left: [ + MovePane( + Some( + Left, + ), + ), + ], + Down: [ + MovePane( + Some( + Down, + ), + ), + ], + Up: [ + MovePane( + Some( + Up, + ), + ), + ], + Right: [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MovePane( + Some( + Left, + ), + ), + ], + Char( + 'j', + ): [ + MovePane( + Some( + Down, + ), + ), + ], + Char( + 'k', + ): [ + MovePane( + Some( + Up, + ), + ), + ], + Char( + 'l', + ): [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + 'n', + ): [ + MovePane( + None, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + BackTab: [ + MovePane( + None, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tmux: { + Left: [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Down: [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Up: [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Right: [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '%', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + ',', + ): [ + SwitchToMode( + RenameTab, + ), + ], + Char( + '[', + ): [ + SwitchToMode( + Scroll, + ), + ], + Char( + '\\', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'n', + ): [ + GoToNextTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'o', + ): [ + FocusNextPane, + ], + Char( + 'p', + ): [ + GoToPreviousTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + Write( + [ + 2, + ], + ), + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + }, + options: Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, + }, + themes: {}, + plugins: { + PluginTag( + "compact-bar", + ): PluginConfig { + path: "compact-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "compact-bar", + ), + ), + }, + PluginTag( + "status-bar", + ): PluginConfig { + path: "status-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + PluginTag( + "strider", + ): PluginConfig { + path: "strider", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "strider", + ), + ), + }, + PluginTag( + "tab-bar", + ): PluginConfig { + path: "tab-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + }, + ui: UiConfig { + pane_frames: FrameConfig { + rounded_corners: false, + }, + }, + env: {}, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap new file mode 100644 index 0000000000..33eeb4ce24 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -0,0 +1,2949 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 593 +expression: "format!(\"{:#?}\", config)" +--- +Config { + keybinds: { + Normal: { + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Char( + 'y', + ), + ): [ + Run( + RunCommandAction { + command: "diskonaut", + args: [], + cwd: Some( + "/home/aram", + ), + direction: None, + }, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + }, + Locked: { + Ctrl( + 'g', + ): [ + SwitchToMode( + Normal, + ), + ], + }, + Resize: { + Left: [ + Resize( + Left, + ), + ], + Down: [ + Resize( + Down, + ), + ], + Up: [ + Resize( + Up, + ), + ], + Right: [ + Resize( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '+', + ): [ + Resize( + Increase, + ), + ], + Char( + '-', + ): [ + Resize( + Decrease, + ), + ], + Char( + '=', + ): [ + Resize( + Increase, + ), + ], + Char( + 'h', + ): [ + Resize( + Left, + ), + ], + Char( + 'j', + ): [ + Resize( + Down, + ), + ], + Char( + 'k', + ): [ + Resize( + Up, + ), + ], + Char( + 'l', + ): [ + Resize( + Right, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Pane: { + Left: [ + MoveFocus( + Left, + ), + ], + Down: [ + MoveFocus( + Down, + ), + ], + Up: [ + MoveFocus( + Up, + ), + ], + Right: [ + MoveFocus( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SwitchToMode( + RenamePane, + ), + ], + Char( + 'd', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'e', + ): [ + TogglePaneEmbedOrFloating, + SwitchToMode( + Normal, + ), + ], + Char( + 'f', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + ], + Char( + 'n', + ): [ + NewPane( + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'p', + ): [ + SwitchFocus, + ], + Char( + 'r', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'w', + ): [ + ToggleFloatingPanes, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseFocus, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + TogglePaneFrames, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tab: { + Left: [ + GoToPreviousTab, + ], + Down: [ + GoToNextTab, + ], + Up: [ + GoToPreviousTab, + ], + Right: [ + GoToNextTab, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '1', + ): [ + GoToTab( + 1, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '2', + ): [ + GoToTab( + 2, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '3', + ): [ + GoToTab( + 3, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '4', + ): [ + GoToTab( + 4, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '5', + ): [ + GoToTab( + 5, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '6', + ): [ + GoToTab( + 6, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '7', + ): [ + GoToTab( + 7, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '8', + ): [ + GoToTab( + 8, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '9', + ): [ + GoToTab( + 9, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + GoToPreviousTab, + ], + Char( + 'j', + ): [ + GoToNextTab, + ], + Char( + 'k', + ): [ + GoToPreviousTab, + ], + Char( + 'l', + ): [ + GoToNextTab, + ], + Char( + 'n', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'r', + ): [ + SwitchToMode( + RenameTab, + ), + TabNameInput( + [ + 0, + ], + ), + ], + Char( + 's', + ): [ + ToggleActiveSyncTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseTab, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Normal, + ), + ], + BackTab: [ + ToggleTab, + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Scroll: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'e', + ): [ + EditScrollback, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 's', + ): [ + SwitchToMode( + EnterSearch, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + EnterSearch: { + Char( + '\n', + ): [ + SwitchToMode( + Search, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Scroll, + ), + ], + Esc: [ + SwitchToMode( + Scroll, + ), + ], + }, + Search: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SearchToggleOption( + CaseSensitivity, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 'n', + ): [ + Search( + Down, + ), + ], + Char( + 'o', + ): [ + SearchToggleOption( + WholeWord, + ), + ], + Char( + 'p', + ): [ + Search( + Up, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Char( + 'w', + ): [ + SearchToggleOption( + Wrap, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + RenameTab: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenameTab, + SwitchToMode( + Tab, + ), + ], + }, + RenamePane: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenamePane, + SwitchToMode( + Pane, + ), + ], + }, + Session: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Move: { + Left: [ + MovePane( + Some( + Left, + ), + ), + ], + Down: [ + MovePane( + Some( + Down, + ), + ), + ], + Up: [ + MovePane( + Some( + Up, + ), + ), + ], + Right: [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MovePane( + Some( + Left, + ), + ), + ], + Char( + 'j', + ): [ + MovePane( + Some( + Down, + ), + ), + ], + Char( + 'k', + ): [ + MovePane( + Some( + Up, + ), + ), + ], + Char( + 'l', + ): [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + 'n', + ): [ + MovePane( + None, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + BackTab: [ + MovePane( + None, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tmux: { + Left: [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Down: [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Up: [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Right: [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '%', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + ',', + ): [ + SwitchToMode( + RenameTab, + ), + ], + Char( + '[', + ): [ + SwitchToMode( + Scroll, + ), + ], + Char( + '\\', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'n', + ): [ + GoToNextTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'o', + ): [ + FocusNextPane, + ], + Char( + 'p', + ): [ + GoToPreviousTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + Write( + [ + 2, + ], + ), + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + }, + options: Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, + }, + themes: {}, + plugins: { + PluginTag( + "compact-bar", + ): PluginConfig { + path: "compact-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "compact-bar", + ), + ), + }, + PluginTag( + "status-bar", + ): PluginConfig { + path: "status-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + PluginTag( + "strider", + ): PluginConfig { + path: "strider", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "strider", + ), + ), + }, + PluginTag( + "tab-bar", + ): PluginConfig { + path: "tab-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + }, + ui: UiConfig { + pane_frames: FrameConfig { + rounded_corners: false, + }, + }, + env: { + "CONFIG_ENV_VAR": "do not override me", + "LAYOUT_ENV_VAR": "make sure I'm also here", + "MY_ENV_VAR": "from layout", + }, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap new file mode 100644 index 0000000000..fd8007affb --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap @@ -0,0 +1,149 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 625 +expression: "format!(\"{:#?}\", config)" +--- +Config { + keybinds: { + Normal: { + Char( + 'b', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Resize, + ), + ], + }, + Resize: { + Char( + 'b', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Resize, + ), + ], + }, + Scroll: { + Char( + 'b', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Resize, + ), + ], + }, + }, + options: Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, + }, + themes: {}, + plugins: { + PluginTag( + "compact-bar", + ): PluginConfig { + path: "compact-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "compact-bar", + ), + ), + }, + PluginTag( + "status-bar", + ): PluginConfig { + path: "status-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + PluginTag( + "strider", + ): PluginConfig { + path: "strider", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "strider", + ), + ), + }, + PluginTag( + "tab-bar", + ): PluginConfig { + path: "tab-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + }, + ui: UiConfig { + pane_frames: FrameConfig { + rounded_corners: false, + }, + }, + env: {}, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap new file mode 100644 index 0000000000..7b5cfc5a25 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap @@ -0,0 +1,49 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 492 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: Some( + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [ + Layout { + direction: Horizontal, + pane_name: None, + parts: Panes( + [], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ], + ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + focused_tab_index: None, + template: None, + }, + ), +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap new file mode 100644 index 0000000000..d8fc787924 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap @@ -0,0 +1,27 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 567 +expression: "format!(\"{:#?}\", options)" +--- +Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: Some( + false, + ), + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap new file mode 100644 index 0000000000..806c2d8a28 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap @@ -0,0 +1,2959 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 609 +expression: "format!(\"{:#?}\", config)" +--- +Config { + keybinds: { + Normal: { + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Char( + 'y', + ), + ): [ + Run( + RunCommandAction { + command: "diskonaut", + args: [], + cwd: Some( + "/home/aram", + ), + direction: None, + }, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + }, + Locked: { + Ctrl( + 'g', + ): [ + SwitchToMode( + Normal, + ), + ], + }, + Resize: { + Left: [ + Resize( + Left, + ), + ], + Down: [ + Resize( + Down, + ), + ], + Up: [ + Resize( + Up, + ), + ], + Right: [ + Resize( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '+', + ): [ + Resize( + Increase, + ), + ], + Char( + '-', + ): [ + Resize( + Decrease, + ), + ], + Char( + '=', + ): [ + Resize( + Increase, + ), + ], + Char( + 'h', + ): [ + Resize( + Left, + ), + ], + Char( + 'j', + ): [ + Resize( + Down, + ), + ], + Char( + 'k', + ): [ + Resize( + Up, + ), + ], + Char( + 'l', + ): [ + Resize( + Right, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Pane: { + Left: [ + MoveFocus( + Left, + ), + ], + Down: [ + MoveFocus( + Down, + ), + ], + Up: [ + MoveFocus( + Up, + ), + ], + Right: [ + MoveFocus( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SwitchToMode( + RenamePane, + ), + ], + Char( + 'd', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'e', + ): [ + TogglePaneEmbedOrFloating, + SwitchToMode( + Normal, + ), + ], + Char( + 'f', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + ], + Char( + 'n', + ): [ + NewPane( + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'p', + ): [ + SwitchFocus, + ], + Char( + 'r', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'w', + ): [ + ToggleFloatingPanes, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseFocus, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + TogglePaneFrames, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tab: { + Left: [ + GoToPreviousTab, + ], + Down: [ + GoToNextTab, + ], + Up: [ + GoToPreviousTab, + ], + Right: [ + GoToNextTab, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '1', + ): [ + GoToTab( + 1, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '2', + ): [ + GoToTab( + 2, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '3', + ): [ + GoToTab( + 3, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '4', + ): [ + GoToTab( + 4, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '5', + ): [ + GoToTab( + 5, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '6', + ): [ + GoToTab( + 6, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '7', + ): [ + GoToTab( + 7, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '8', + ): [ + GoToTab( + 8, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '9', + ): [ + GoToTab( + 9, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + GoToPreviousTab, + ], + Char( + 'j', + ): [ + GoToNextTab, + ], + Char( + 'k', + ): [ + GoToPreviousTab, + ], + Char( + 'l', + ): [ + GoToNextTab, + ], + Char( + 'n', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'r', + ): [ + SwitchToMode( + RenameTab, + ), + TabNameInput( + [ + 0, + ], + ), + ], + Char( + 's', + ): [ + ToggleActiveSyncTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseTab, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Normal, + ), + ], + BackTab: [ + ToggleTab, + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Scroll: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'e', + ): [ + EditScrollback, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 's', + ): [ + SwitchToMode( + EnterSearch, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + EnterSearch: { + Char( + '\n', + ): [ + SwitchToMode( + Search, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Scroll, + ), + ], + Esc: [ + SwitchToMode( + Scroll, + ), + ], + }, + Search: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SearchToggleOption( + CaseSensitivity, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 'n', + ): [ + Search( + Down, + ), + ], + Char( + 'o', + ): [ + SearchToggleOption( + WholeWord, + ), + ], + Char( + 'p', + ): [ + Search( + Up, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Char( + 'w', + ): [ + SearchToggleOption( + Wrap, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + RenameTab: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenameTab, + SwitchToMode( + Tab, + ), + ], + }, + RenamePane: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenamePane, + SwitchToMode( + Pane, + ), + ], + }, + Session: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Move: { + Left: [ + MovePane( + Some( + Left, + ), + ), + ], + Down: [ + MovePane( + Some( + Down, + ), + ), + ], + Up: [ + MovePane( + Some( + Up, + ), + ), + ], + Right: [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MovePane( + Some( + Left, + ), + ), + ], + Char( + 'j', + ): [ + MovePane( + Some( + Down, + ), + ), + ], + Char( + 'k', + ): [ + MovePane( + Some( + Up, + ), + ), + ], + Char( + 'l', + ): [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + 'n', + ): [ + MovePane( + None, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + BackTab: [ + MovePane( + None, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tmux: { + Left: [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Down: [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Up: [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Right: [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '%', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + ',', + ): [ + SwitchToMode( + RenameTab, + ), + ], + Char( + '[', + ): [ + SwitchToMode( + Scroll, + ), + ], + Char( + '\\', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'n', + ): [ + GoToNextTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'o', + ): [ + FocusNextPane, + ], + Char( + 'p', + ): [ + GoToPreviousTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + Write( + [ + 2, + ], + ), + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + }, + options: Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, + }, + themes: {}, + plugins: { + PluginTag( + "compact-bar", + ): PluginConfig { + path: "compact-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "compact-bar", + ), + ), + }, + PluginTag( + "some-other-plugin", + ): PluginConfig { + path: "i-am-defined-in-the-layout", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "some-other-plugin", + ), + ), + }, + PluginTag( + "status-bar", + ): PluginConfig { + path: "status-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + PluginTag( + "strider", + ): PluginConfig { + path: "strider", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "strider", + ), + ), + }, + PluginTag( + "tab-bar", + ): PluginConfig { + path: "tab-bar-from-layout", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + }, + ui: UiConfig { + pane_frames: FrameConfig { + rounded_corners: false, + }, + }, + env: {}, +} 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 new file mode 100644 index 0000000000..a1337407bd --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -0,0 +1,3249 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 617 +expression: "format!(\"{:#?}\", config)" +--- +Config { + keybinds: { + Normal: { + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Char( + 'y', + ), + ): [ + Run( + RunCommandAction { + command: "diskonaut", + args: [], + cwd: Some( + "/home/aram", + ), + direction: None, + }, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + }, + Locked: { + Ctrl( + 'g', + ): [ + SwitchToMode( + Normal, + ), + ], + }, + Resize: { + Left: [ + Resize( + Left, + ), + ], + Down: [ + Resize( + Down, + ), + ], + Up: [ + Resize( + Up, + ), + ], + Right: [ + Resize( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '+', + ): [ + Resize( + Increase, + ), + ], + Char( + '-', + ): [ + Resize( + Decrease, + ), + ], + Char( + '=', + ): [ + Resize( + Increase, + ), + ], + Char( + 'h', + ): [ + Resize( + Left, + ), + ], + Char( + 'j', + ): [ + Resize( + Down, + ), + ], + Char( + 'k', + ): [ + Resize( + Up, + ), + ], + Char( + 'l', + ): [ + Resize( + Right, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Pane: { + Left: [ + MoveFocus( + Left, + ), + ], + Down: [ + MoveFocus( + Down, + ), + ], + Up: [ + MoveFocus( + Up, + ), + ], + Right: [ + MoveFocus( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SwitchToMode( + RenamePane, + ), + ], + Char( + 'd', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'e', + ): [ + TogglePaneEmbedOrFloating, + SwitchToMode( + Normal, + ), + ], + Char( + 'f', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + ], + Char( + 'n', + ): [ + NewPane( + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'p', + ): [ + SwitchFocus, + ], + Char( + 'r', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'w', + ): [ + ToggleFloatingPanes, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseFocus, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + TogglePaneFrames, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tab: { + Left: [ + GoToPreviousTab, + ], + Down: [ + GoToNextTab, + ], + Up: [ + GoToPreviousTab, + ], + Right: [ + GoToNextTab, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '1', + ): [ + GoToTab( + 1, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '2', + ): [ + GoToTab( + 2, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '3', + ): [ + GoToTab( + 3, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '4', + ): [ + GoToTab( + 4, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '5', + ): [ + GoToTab( + 5, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '6', + ): [ + GoToTab( + 6, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '7', + ): [ + GoToTab( + 7, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '8', + ): [ + GoToTab( + 8, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '9', + ): [ + GoToTab( + 9, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + GoToPreviousTab, + ], + Char( + 'j', + ): [ + GoToNextTab, + ], + Char( + 'k', + ): [ + GoToPreviousTab, + ], + Char( + 'l', + ): [ + GoToNextTab, + ], + Char( + 'n', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'r', + ): [ + SwitchToMode( + RenameTab, + ), + TabNameInput( + [ + 0, + ], + ), + ], + Char( + 's', + ): [ + ToggleActiveSyncTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseTab, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Normal, + ), + ], + BackTab: [ + ToggleTab, + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Scroll: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'e', + ): [ + EditScrollback, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 's', + ): [ + SwitchToMode( + EnterSearch, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + EnterSearch: { + Char( + '\n', + ): [ + SwitchToMode( + Search, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Scroll, + ), + ], + Esc: [ + SwitchToMode( + Scroll, + ), + ], + }, + Search: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SearchToggleOption( + CaseSensitivity, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 'n', + ): [ + Search( + Down, + ), + ], + Char( + 'o', + ): [ + SearchToggleOption( + WholeWord, + ), + ], + Char( + 'p', + ): [ + Search( + Up, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Char( + 'w', + ): [ + SearchToggleOption( + Wrap, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + RenameTab: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenameTab, + SwitchToMode( + Tab, + ), + ], + }, + RenamePane: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenamePane, + SwitchToMode( + Pane, + ), + ], + }, + Session: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Move: { + Left: [ + MovePane( + Some( + Left, + ), + ), + ], + Down: [ + MovePane( + Some( + Down, + ), + ), + ], + Up: [ + MovePane( + Some( + Up, + ), + ), + ], + Right: [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MovePane( + Some( + Left, + ), + ), + ], + Char( + 'j', + ): [ + MovePane( + Some( + Down, + ), + ), + ], + Char( + 'k', + ): [ + MovePane( + Some( + Up, + ), + ), + ], + Char( + 'l', + ): [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + 'n', + ): [ + MovePane( + None, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + BackTab: [ + MovePane( + None, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tmux: { + Left: [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Down: [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Up: [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Right: [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '%', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + ',', + ): [ + SwitchToMode( + RenameTab, + ), + ], + Char( + '[', + ): [ + SwitchToMode( + Scroll, + ), + ], + Char( + '\\', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'n', + ): [ + GoToNextTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'o', + ): [ + FocusNextPane, + ], + Char( + 'p', + ): [ + GoToPreviousTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + Write( + [ + 2, + ], + ), + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + }, + options: Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, + }, + themes: { + "other-theme-from-config": Theme { + palette: Palette { + source: Default, + theme_hue: Dark, + fg: Rgb( + ( + 2, + 2, + 2, + ), + ), + bg: Rgb( + ( + 2, + 2, + 2, + ), + ), + black: Rgb( + ( + 2, + 2, + 2, + ), + ), + red: Rgb( + ( + 2, + 2, + 2, + ), + ), + green: Rgb( + ( + 2, + 2, + 2, + ), + ), + yellow: Rgb( + ( + 2, + 2, + 2, + ), + ), + blue: Rgb( + ( + 2, + 2, + 2, + ), + ), + magenta: Rgb( + ( + 2, + 2, + 2, + ), + ), + cyan: Rgb( + ( + 2, + 2, + 2, + ), + ), + white: Rgb( + ( + 2, + 2, + 2, + ), + ), + orange: Rgb( + ( + 2, + 2, + 2, + ), + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + }, + "theme-from-config": Theme { + palette: Palette { + source: Default, + theme_hue: Dark, + fg: Rgb( + ( + 1, + 1, + 1, + ), + ), + bg: Rgb( + ( + 1, + 1, + 1, + ), + ), + black: Rgb( + ( + 1, + 1, + 1, + ), + ), + red: Rgb( + ( + 1, + 1, + 1, + ), + ), + green: Rgb( + ( + 1, + 1, + 1, + ), + ), + yellow: Rgb( + ( + 1, + 1, + 1, + ), + ), + blue: Rgb( + ( + 1, + 1, + 1, + ), + ), + magenta: Rgb( + ( + 1, + 1, + 1, + ), + ), + cyan: Rgb( + ( + 1, + 1, + 1, + ), + ), + white: Rgb( + ( + 1, + 1, + 1, + ), + ), + orange: Rgb( + ( + 1, + 1, + 1, + ), + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + }, + "theme-from-layout": Theme { + palette: Palette { + source: Default, + theme_hue: Dark, + fg: Rgb( + ( + 1, + 1, + 1, + ), + ), + bg: Rgb( + ( + 1, + 1, + 1, + ), + ), + black: Rgb( + ( + 1, + 1, + 1, + ), + ), + red: Rgb( + ( + 1, + 1, + 1, + ), + ), + green: Rgb( + ( + 1, + 1, + 1, + ), + ), + yellow: Rgb( + ( + 1, + 1, + 1, + ), + ), + blue: Rgb( + ( + 1, + 1, + 1, + ), + ), + magenta: Rgb( + ( + 1, + 1, + 1, + ), + ), + cyan: Rgb( + ( + 1, + 1, + 1, + ), + ), + white: Rgb( + ( + 1, + 1, + 1, + ), + ), + orange: Rgb( + ( + 1, + 1, + 1, + ), + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + }, + }, + plugins: { + PluginTag( + "compact-bar", + ): PluginConfig { + path: "compact-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "compact-bar", + ), + ), + }, + PluginTag( + "status-bar", + ): PluginConfig { + path: "status-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + PluginTag( + "strider", + ): PluginConfig { + path: "strider", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "strider", + ), + ), + }, + PluginTag( + "tab-bar", + ): PluginConfig { + path: "tab-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + }, + ui: UiConfig { + pane_frames: FrameConfig { + rounded_corners: false, + }, + }, + env: {}, +} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap new file mode 100644 index 0000000000..b9852a90b1 --- /dev/null +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -0,0 +1,2945 @@ +--- +source: zellij-utils/src/setup.rs +assertion_line: 601 +expression: "format!(\"{:#?}\", config)" +--- +Config { + keybinds: { + Normal: { + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Char( + 'y', + ), + ): [ + Run( + RunCommandAction { + command: "diskonaut", + args: [], + cwd: Some( + "/home/aram", + ), + direction: None, + }, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + }, + Locked: { + Ctrl( + 'g', + ): [ + SwitchToMode( + Normal, + ), + ], + }, + Resize: { + Left: [ + Resize( + Left, + ), + ], + Down: [ + Resize( + Down, + ), + ], + Up: [ + Resize( + Up, + ), + ], + Right: [ + Resize( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '+', + ): [ + Resize( + Increase, + ), + ], + Char( + '-', + ): [ + Resize( + Decrease, + ), + ], + Char( + '=', + ): [ + Resize( + Increase, + ), + ], + Char( + 'h', + ): [ + Resize( + Left, + ), + ], + Char( + 'j', + ): [ + Resize( + Down, + ), + ], + Char( + 'k', + ): [ + Resize( + Up, + ), + ], + Char( + 'l', + ): [ + Resize( + Right, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Pane: { + Left: [ + MoveFocus( + Left, + ), + ], + Down: [ + MoveFocus( + Down, + ), + ], + Up: [ + MoveFocus( + Up, + ), + ], + Right: [ + MoveFocus( + Right, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SwitchToMode( + RenamePane, + ), + ], + Char( + 'd', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'e', + ): [ + TogglePaneEmbedOrFloating, + SwitchToMode( + Normal, + ), + ], + Char( + 'f', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + ], + Char( + 'n', + ): [ + NewPane( + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'p', + ): [ + SwitchFocus, + ], + Char( + 'r', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'w', + ): [ + ToggleFloatingPanes, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseFocus, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + TogglePaneFrames, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tab: { + Left: [ + GoToPreviousTab, + ], + Down: [ + GoToNextTab, + ], + Up: [ + GoToPreviousTab, + ], + Right: [ + GoToNextTab, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '1', + ): [ + GoToTab( + 1, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '2', + ): [ + GoToTab( + 2, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '3', + ): [ + GoToTab( + 3, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '4', + ): [ + GoToTab( + 4, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '5', + ): [ + GoToTab( + 5, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '6', + ): [ + GoToTab( + 6, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '7', + ): [ + GoToTab( + 7, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '8', + ): [ + GoToTab( + 8, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '9', + ): [ + GoToTab( + 9, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + GoToPreviousTab, + ], + Char( + 'j', + ): [ + GoToNextTab, + ], + Char( + 'k', + ): [ + GoToPreviousTab, + ], + Char( + 'l', + ): [ + GoToNextTab, + ], + Char( + 'n', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'r', + ): [ + SwitchToMode( + RenameTab, + ), + TabNameInput( + [ + 0, + ], + ), + ], + Char( + 's', + ): [ + ToggleActiveSyncTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'x', + ): [ + CloseTab, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Normal, + ), + ], + BackTab: [ + ToggleTab, + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Scroll: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'e', + ): [ + EditScrollback, + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 's', + ): [ + SwitchToMode( + EnterSearch, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + EnterSearch: { + Char( + '\n', + ): [ + SwitchToMode( + Search, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Scroll, + ), + ], + Esc: [ + SwitchToMode( + Scroll, + ), + ], + }, + Search: { + PageDown: [ + PageScrollDown, + ], + PageUp: [ + PageScrollUp, + ], + Left: [ + PageScrollUp, + ], + Down: [ + ScrollDown, + ], + Up: [ + ScrollUp, + ], + Right: [ + PageScrollDown, + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + SearchToggleOption( + CaseSensitivity, + ), + ], + Char( + 'd', + ): [ + HalfPageScrollDown, + ], + Char( + 'h', + ): [ + PageScrollUp, + ], + Char( + 'j', + ): [ + ScrollDown, + ], + Char( + 'k', + ): [ + ScrollUp, + ], + Char( + 'l', + ): [ + PageScrollDown, + ], + Char( + 'n', + ): [ + Search( + Down, + ), + ], + Char( + 'o', + ): [ + SearchToggleOption( + WholeWord, + ), + ], + Char( + 'p', + ): [ + Search( + Up, + ), + ], + Char( + 'u', + ): [ + HalfPageScrollUp, + ], + Char( + 'w', + ): [ + SearchToggleOption( + Wrap, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + PageScrollUp, + ], + Ctrl( + 'c', + ): [ + ScrollToBottom, + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'f', + ): [ + PageScrollDown, + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + RenameTab: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenameTab, + SwitchToMode( + Tab, + ), + ], + }, + RenamePane: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'c', + ): [ + SwitchToMode( + Normal, + ), + ], + Esc: [ + UndoRenamePane, + SwitchToMode( + Pane, + ), + ], + }, + Session: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Move: { + Left: [ + MovePane( + Some( + Left, + ), + ), + ], + Down: [ + MovePane( + Some( + Down, + ), + ), + ], + Up: [ + MovePane( + Some( + Up, + ), + ), + ], + Right: [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + 'h', + ): [ + MovePane( + Some( + Left, + ), + ), + ], + Char( + 'j', + ): [ + MovePane( + Some( + Down, + ), + ), + ], + Char( + 'k', + ): [ + MovePane( + Some( + Up, + ), + ), + ], + Char( + 'l', + ): [ + MovePane( + Some( + Right, + ), + ), + ], + Char( + 'n', + ): [ + MovePane( + None, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + BackTab: [ + MovePane( + None, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + Tmux: { + Left: [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Down: [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Up: [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Right: [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + '%', + ): [ + NewPane( + Some( + Right, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + ',', + ): [ + SwitchToMode( + RenameTab, + ), + ], + Char( + '[', + ): [ + SwitchToMode( + Scroll, + ), + ], + Char( + '\\', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'c', + ): [ + NewTab( + None, + None, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'd', + ): [ + Detach, + ], + Char( + 'h', + ): [ + MoveFocus( + Left, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'j', + ): [ + MoveFocus( + Down, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'k', + ): [ + MoveFocus( + Up, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'l', + ): [ + MoveFocus( + Right, + ), + SwitchToMode( + Normal, + ), + ], + Char( + 'n', + ): [ + GoToNextTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'o', + ): [ + FocusNextPane, + ], + Char( + 'p', + ): [ + GoToPreviousTab, + SwitchToMode( + Normal, + ), + ], + Char( + 'z', + ): [ + ToggleFocusFullscreen, + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + Write( + [ + 2, + ], + ), + SwitchToMode( + Normal, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, + }, + options: Options { + simplified_ui: None, + theme: None, + default_mode: None, + default_shell: None, + default_layout: None, + layout_dir: None, + theme_dir: None, + mouse_mode: None, + pane_frames: None, + mirror_session: None, + on_force_close: None, + scroll_buffer_size: None, + copy_command: None, + copy_clipboard: None, + copy_on_select: None, + scrollback_editor: None, + session_name: None, + attach_to_session: None, + }, + themes: {}, + plugins: { + PluginTag( + "compact-bar", + ): PluginConfig { + path: "compact-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "compact-bar", + ), + ), + }, + PluginTag( + "status-bar", + ): PluginConfig { + path: "status-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + PluginTag( + "strider", + ): PluginConfig { + path: "strider", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "strider", + ), + ), + }, + PluginTag( + "tab-bar", + ): PluginConfig { + path: "tab-bar", + run: Pane( + None, + ), + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + }, + ui: UiConfig { + pane_frames: FrameConfig { + rounded_corners: true, + }, + }, + env: {}, +} diff --git a/zellij-utils/src/test-fixtures/config-with-env-vars.kdl b/zellij-utils/src/test-fixtures/config-with-env-vars.kdl new file mode 100644 index 0000000000..87c94e6c3d --- /dev/null +++ b/zellij-utils/src/test-fixtures/config-with-env-vars.kdl @@ -0,0 +1,4 @@ +env { + CONFIG_ENV_VAR "do not override me" + MY_ENV_VAR "from config" +} diff --git a/zellij-utils/src/test-fixtures/config-with-keybindings-config.kdl b/zellij-utils/src/test-fixtures/config-with-keybindings-config.kdl new file mode 100644 index 0000000000..84f98de577 --- /dev/null +++ b/zellij-utils/src/test-fixtures/config-with-keybindings-config.kdl @@ -0,0 +1,11 @@ +layout +keybinds clear-defaults=true { + normal { + bind "b" { SwitchToMode "Locked"; } + bind "Ctrl c" { SwitchToMode "Resize"; } + } + resize { + bind "b" { SwitchToMode "Locked"; } + bind "Ctrl c" { SwitchToMode "Resize"; } + } +} diff --git a/zellij-utils/src/test-fixtures/config-with-plugins-config.kdl b/zellij-utils/src/test-fixtures/config-with-plugins-config.kdl new file mode 100644 index 0000000000..513e4de033 --- /dev/null +++ b/zellij-utils/src/test-fixtures/config-with-plugins-config.kdl @@ -0,0 +1,6 @@ +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} diff --git a/zellij-utils/src/test-fixtures/config-with-themes-config.kdl b/zellij-utils/src/test-fixtures/config-with-themes-config.kdl new file mode 100644 index 0000000000..e0ed0aa387 --- /dev/null +++ b/zellij-utils/src/test-fixtures/config-with-themes-config.kdl @@ -0,0 +1,28 @@ +themes { + other-theme-from-config { + bg 2 2 2 + red 2 2 2 + green 2 2 2 + yellow 2 2 2 + blue 2 2 2 + magenta 2 2 2 + orange 2 2 2 + fg 2 2 2 + cyan 2 2 2 + black 2 2 2 + white 2 2 2 + } + theme-from-config { + bg 2 2 2 + red 2 2 2 + green 2 2 2 + yellow 2 2 2 + blue 2 2 2 + magenta 2 2 2 + orange 2 2 2 + fg 2 2 2 + cyan 2 2 2 + black 2 2 2 + white 2 2 2 + } +} diff --git a/zellij-utils/src/test-fixtures/config-with-ui-config.kdl b/zellij-utils/src/test-fixtures/config-with-ui-config.kdl new file mode 100644 index 0000000000..fbd518fad8 --- /dev/null +++ b/zellij-utils/src/test-fixtures/config-with-ui-config.kdl @@ -0,0 +1,5 @@ +ui { + pane_frames { + rounded_corners false + } +} diff --git a/zellij-utils/src/test-fixtures/layout-with-env-vars.kdl b/zellij-utils/src/test-fixtures/layout-with-env-vars.kdl new file mode 100644 index 0000000000..0ef6721aed --- /dev/null +++ b/zellij-utils/src/test-fixtures/layout-with-env-vars.kdl @@ -0,0 +1,5 @@ +layout +env { + LAYOUT_ENV_VAR "make sure I'm also here" + MY_ENV_VAR "from layout" +} diff --git a/zellij-utils/src/test-fixtures/layout-with-keybindings-config.kdl b/zellij-utils/src/test-fixtures/layout-with-keybindings-config.kdl new file mode 100644 index 0000000000..62c5cd8b79 --- /dev/null +++ b/zellij-utils/src/test-fixtures/layout-with-keybindings-config.kdl @@ -0,0 +1,11 @@ +layout +keybinds { + normal { + bind "b" { SwitchToMode "Session"; } + bind "Ctrl b" { SwitchToMode "Resize"; } + } + scroll { + bind "b" { SwitchToMode "Locked"; } + bind "Ctrl c" { SwitchToMode "Resize"; } + } +} diff --git a/zellij-utils/src/test-fixtures/layout-with-options.kdl b/zellij-utils/src/test-fixtures/layout-with-options.kdl new file mode 100644 index 0000000000..408003b178 --- /dev/null +++ b/zellij-utils/src/test-fixtures/layout-with-options.kdl @@ -0,0 +1,2 @@ +layout +pane_frames false diff --git a/zellij-utils/src/test-fixtures/layout-with-plugins-config.kdl b/zellij-utils/src/test-fixtures/layout-with-plugins-config.kdl new file mode 100644 index 0000000000..95b177abbf --- /dev/null +++ b/zellij-utils/src/test-fixtures/layout-with-plugins-config.kdl @@ -0,0 +1,5 @@ +layout +plugins { + some-other-plugin { path "i-am-defined-in-the-layout"; } + tab-bar { path "tab-bar-from-layout"; } +} diff --git a/zellij-utils/src/test-fixtures/layout-with-themes-config.kdl b/zellij-utils/src/test-fixtures/layout-with-themes-config.kdl new file mode 100644 index 0000000000..7fc57aa0d4 --- /dev/null +++ b/zellij-utils/src/test-fixtures/layout-with-themes-config.kdl @@ -0,0 +1,30 @@ +layout +themes { + theme-from-config { + bg 1 1 1 + red 1 1 1 + green 1 1 1 + yellow 1 1 1 + blue 1 1 1 + magenta 1 1 1 + orange 1 1 1 + fg 1 1 1 + cyan 1 1 1 + black 1 1 1 + white 1 1 1 + } + theme-from-layout { + bg 1 1 1 + red 1 1 1 + green 1 1 1 + yellow 1 1 1 + blue 1 1 1 + magenta 1 1 1 + orange 1 1 1 + fg 1 1 1 + cyan 1 1 1 + black 1 1 1 + white 1 1 1 + } +} + diff --git a/zellij-utils/src/test-fixtures/layout-with-ui-config.kdl b/zellij-utils/src/test-fixtures/layout-with-ui-config.kdl new file mode 100644 index 0000000000..dfaeee91eb --- /dev/null +++ b/zellij-utils/src/test-fixtures/layout-with-ui-config.kdl @@ -0,0 +1,6 @@ +layout +ui { + pane_frames { + rounded_corners true + } +} From d139c69deb1a9269e2c8076a614982ddd35df423 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 2 Sep 2022 09:39:24 +0200 Subject: [PATCH 16/55] work --- zellij-utils/src/input/layout.rs | 70 +++++++++++-- zellij-utils/src/kdl/kdl_layout_parser.rs | 114 ++++++++++------------ 2 files changed, 115 insertions(+), 69 deletions(-) diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index eaf16ccf7d..7698938370 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -110,22 +110,74 @@ impl fmt::Display for RunPluginLocation { } } -// The layout struct ultimately used to build the layouts. +// // The layout struct ultimately used to build the layouts. +// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] +// pub struct Layout { +// pub direction: SplitDirection, +// #[serde(default)] +// pub pane_name: Option, +// #[serde(default)] +// pub parts: LayoutParts, +// pub split_size: Option, +// pub run: Option, +// #[serde(default)] +// pub borderless: bool, +// pub focus: Option, +// pub external_children_index: Option, +// pub focused_tab_index: Option, +// pub template: Option>, +// } + +// TODO: CONTINUE HERE - keep following the compiler in kdl_layout_parser.rs +// to work with these new refactored structs #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] pub struct Layout { - pub direction: SplitDirection, - #[serde(default)] - pub pane_name: Option, - #[serde(default)] - pub parts: LayoutParts, + pub tabs: Vec<(Option, PaneLayout)>, + pub focused_tab_index: Option, + pub template: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] +pub struct PaneLayout { + pub children_split_direction: SplitDirection, + pub name: Option, + pub children: Vec, pub split_size: Option, pub run: Option, - #[serde(default)] pub borderless: bool, pub focus: Option, pub external_children_index: Option, - pub focused_tab_index: Option, - pub template: Option>, +} + +impl PaneLayout { + pub fn insert_children_layout(&mut self, children_layout: &mut PaneLayout) -> Result { + // returns true if successfully inserted and false otherwise + match self.external_children_index { + Some(external_children_index) => { + self.children.insert(external_children_index, children_layout.clone()); + self.external_children_index = None; + Ok(true) + }, + None => { + for pane in self.children.iter_mut() { + if pane.insert_children_layout(children_layout)? { + return Ok(true); + } + } + Ok(false) + } + } + } + pub fn children_block_count(&self) -> usize { + let mut count = 0; + if self.external_children_index.is_some() { + count += 1; + } + for pane in self.children { + count += pane.children_block_count(); + } + count + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 436c8ba693..5ba68dccf6 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -12,7 +12,7 @@ use crate::{ input::{ command::RunCommand, config::{ConfigError, LayoutNameInTabError}, - layout::{Layout, LayoutParts, SplitDirection, Run, RunPlugin, RunPluginLocation, SplitSize}, + layout::{Layout, PaneLayout, LayoutParts, SplitDirection, Run, RunPlugin, RunPluginLocation, SplitSize}, plugins::{PluginTag, PluginsConfigError}, }, pane_size::{Dimension, PaneGeom}, @@ -40,10 +40,6 @@ use crate::{ kdl_get_int_property_or_child_value, }; -// use super::{ -// // config::ConfigFromYaml, -// plugins::{PluginTag, PluginsConfigError}, -// }; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; use std::vec::Vec; @@ -58,9 +54,9 @@ use url::Url; pub struct KdlLayoutParser <'a>{ kdl_layout: &'a KdlDocument, - tab_templates: HashMap, - pane_templates: HashMap, - default_tab_template: Option, + tab_templates: HashMap, + pane_templates: HashMap, + default_tab_template: Option, } impl <'a>KdlLayoutParser <'a> { @@ -145,7 +141,7 @@ impl <'a>KdlLayoutParser <'a> { } Ok(run) } - fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { + fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); let pane_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|name| name.to_string()); @@ -168,7 +164,7 @@ impl <'a>KdlLayoutParser <'a> { ..Default::default() }) } - fn parse_pane_node_with_template(&self, kdl_node: &KdlNode, mut pane_layout: Layout) -> Result { + fn parse_pane_node_with_template(&self, kdl_node: &KdlNode, mut pane_layout: PaneLayout) -> Result { let direction = self.parse_split_direction(kdl_node)?; match kdl_children_nodes!(kdl_node) { Some(children) => { @@ -206,47 +202,47 @@ impl <'a>KdlLayoutParser <'a> { let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_or_plugin_block(kdl_node)?; - let direction = self.parse_split_direction(kdl_node)?; + let children_split_direction = self.parse_split_direction(kdl_node)?; let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, None => (None, vec![]) }; - self.pane_templates.insert(template_name, Layout { + self.pane_templates.insert(template_name, PaneLayout { borderless: borderless.unwrap_or_default(), focus, split_size, run, - direction, + children_split_direction, external_children_index, - parts: LayoutParts::Panes(pane_parts), + children: pane_parts, ..Default::default() }); Ok(()) } - fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(Option, Layout), ConfigError> { // String is the tab name + fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(Option, PaneLayout), ConfigError> { // String is the tab name match self.default_tab_template.as_ref().map(|t| t.clone()) { Some(default_tab_template) => { self.parse_tab_node_with_template(kdl_node, default_tab_template) }, None => { let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); - let direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), }; - let pane_parts = match kdl_children_nodes!(kdl_node) { + let children = match kdl_children_nodes!(kdl_node) { Some(children) => self.parse_child_pane_nodes_for_tab(children)?, - None => vec![Layout::default()], + None => vec![], }; - Ok((tab_name, Layout { - direction, - parts: LayoutParts::Panes(pane_parts), + Ok((tab_name, PaneLayout { + children_split_direction, + children, ..Default::default() })) } } } - fn parse_child_pane_nodes_for_tab(&self, children: &[KdlNode]) -> Result, ConfigError> { + fn parse_child_pane_nodes_for_tab(&self, children: &[KdlNode]) -> Result, ConfigError> { let mut nodes = vec![]; for child in children { if kdl_name!(child) == "pane" { @@ -256,11 +252,11 @@ impl <'a>KdlLayoutParser <'a> { } } if nodes.is_empty() { - nodes.push(Layout::default()); + nodes.push(PaneLayout::default()); } Ok(nodes) } - fn parse_child_pane_nodes_for_pane(&self, children: &[KdlNode]) -> Result<(Option, Vec), ConfigError> { // usize is external_children_index + fn parse_child_pane_nodes_for_pane(&self, children: &[KdlNode]) -> Result<(Option, Vec), ConfigError> { // usize is external_children_index let mut external_children_index = None; let mut nodes = vec![]; for (i, child) in children.iter().enumerate() { @@ -274,14 +270,14 @@ impl <'a>KdlLayoutParser <'a> { } Ok((external_children_index, nodes)) } - fn assert_one_children_block(&self, layout: &Layout) -> Result<(), ConfigError> { + fn assert_one_children_block(&self, layout: &PaneLayout) -> Result<(), ConfigError> { let children_block_count = layout.children_block_count(); if children_block_count != 1 { return Err(ConfigError::KdlParsingError(format!("Layout has {} children blocks, only 1 is allowed", children_block_count))); } Ok(()) } - fn insert_layout_children_or_error(&self, layout: &mut Layout, mut child_panes_layout: Layout) -> Result<(), ConfigError> { + fn insert_layout_children_or_error(&self, layout: &mut PaneLayout, mut child_panes_layout: PaneLayout) -> Result<(), ConfigError> { let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?; if !successfully_inserted { Err(ConfigError::KdlParsingError("This tab template does not have children".into())) @@ -289,18 +285,18 @@ impl <'a>KdlLayoutParser <'a> { Ok(()) } } - fn parse_tab_node_with_template(&mut self, kdl_node: &KdlNode, mut tab_layout: Layout) -> Result<(Option, Layout), ConfigError> { // String is the tab name + fn parse_tab_node_with_template(&mut self, kdl_node: &KdlNode, mut tab_layout: PaneLayout) -> Result<(Option, PaneLayout), ConfigError> { // String is the tab name let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); - let direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), }; match kdl_children_nodes!(kdl_node) { Some(children) => { let child_panes = self.parse_child_pane_nodes_for_tab(children)?; - let child_panes_layout = Layout { - direction, - parts: LayoutParts::Panes(child_panes), + let child_panes_layout = PaneLayout { + children_split_direction, + children: child_panes, ..Default::default() }; self.assert_one_children_block(&tab_layout)?; @@ -308,7 +304,7 @@ impl <'a>KdlLayoutParser <'a> { }, None => { if let Some(index_of_children) = tab_layout.external_children_index { - tab_layout.parts.insert_pane(index_of_children, Layout::default())?; + tab_layout.children.insert(index_of_children, PaneLayout::default()); } } } @@ -325,40 +321,37 @@ impl <'a>KdlLayoutParser <'a> { self.default_tab_template = Some(self.parse_tab_template_node(kdl_node)?); Ok(()) } - fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result { - let direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result { + let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), }; - let mut pane_parts = vec![]; + let mut tab_children = vec![]; let mut external_children_index = None; if let Some(children) = kdl_children_nodes!(kdl_node) { for (i, child) in children.iter().enumerate() { if kdl_name!(child) == "pane" { - pane_parts.push(self.parse_pane_node(child)?); + tab_children.push(self.parse_pane_node(child)?); } else if kdl_name!(child) == "children" { external_children_index = Some(i); } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { - pane_parts.push(self.parse_pane_node_with_template(child, pane_template)?); + tab_children.push(self.parse_pane_node_with_template(child, pane_template)?); } } } - if pane_parts.is_empty() { - pane_parts.push(Layout::default()); - } - Ok(Layout { - direction, - parts: LayoutParts::Panes(pane_parts), + Ok(PaneLayout { + children_split_direction, + children: tab_children, external_children_index, ..Default::default() }) } - fn default_template(&self) -> Result, ConfigError> { + fn default_template(&self) -> Result, ConfigError> { match &self.default_tab_template { Some(template) => { let mut template = template.clone(); if let Some(children_index) = template.external_children_index { - template.parts.insert_pane(children_index, Layout::default())?; + template.children.insert(children_index, PaneLayout::default()) } template.external_children_index = None; Ok(Some(template)) @@ -445,43 +438,44 @@ impl <'a>KdlLayoutParser <'a> { } Ok(()) } - fn layout_with_tabs(&self, tabs: Vec<(Option, Layout)>) -> Result { - let template = self.default_template()?.unwrap_or_else(|| Layout::with_one_pane()); + fn layout_with_tabs(&self, tabs: Vec<(Option, PaneLayout)>) -> Result { + let template = self.default_template()?.unwrap_or_else(|| PaneLayout::default()); + Ok(Layout { - parts: LayoutParts::Tabs(tabs), - template: Some(Box::new(template)), + tabs: tabs, + template: Some(template), ..Default::default() }) } - fn layout_with_one_tab(&self, panes: Vec) -> Result { - let main_tab_layout = Layout { - parts: LayoutParts::Panes(panes.clone()), + fn layout_with_one_tab(&self, panes: Vec) -> Result { + let main_tab_layout = PaneLayout { + children: panes, ..Default::default() }; let default_template = self.default_template()?; - let parts = if default_template.is_none() { + let tabs = if default_template.is_none() { // in this case, the layout will be created as the default template and we don't need // to explicitly place it in the first tab - LayoutParts::default() + vec![] } else { - LayoutParts::Tabs(vec![(None, main_tab_layout.clone())]) + vec![(None, main_tab_layout.clone())] }; let template = default_template.unwrap_or_else(|| main_tab_layout.clone()); // create a layout with one tab that has these child panes Ok(Layout { - parts, - template: Some(Box::new(template)), + tabs, + template: Some(template), ..Default::default() }) } fn layout_with_one_pane(&self) -> Result { - let template = self.default_template()?.unwrap_or_else(|| Layout::with_one_pane()); + let template = self.default_template()?.unwrap_or_else(|| PaneLayout::default()); Ok(Layout { - template: Some(Box::new(template)), + template: Some(template), ..Default::default() }) } - fn populate_layout_child(&mut self, child: &KdlNode, child_tabs: &mut Vec<(Option, Layout)>, child_panes: &mut Vec) -> Result<(), ConfigError> { + fn populate_layout_child(&mut self, child: &KdlNode, child_tabs: &mut Vec<(Option, PaneLayout)>, child_panes: &mut Vec) -> Result<(), ConfigError> { let child_name = kdl_name!(child); if child_name == "pane" { if !child_tabs.is_empty() { From 8237223b7842c22cc25d875dcd9aa89b61b0ac96 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Tue, 6 Sep 2022 16:02:59 +0200 Subject: [PATCH 17/55] tests(layout): add cases and fix bugs --- zellij-server/src/pty.rs | 6 +- zellij-server/src/screen.rs | 6 +- zellij-server/src/tab/mod.rs | 10 +- ...egration_tests__tab_with_basic_layout.snap | 26 + ...gration_tests__tab_with_nested_layout.snap | 26 + ..._tests__tab_with_nested_uneven_layout.snap | 26 + .../src/tab/unit/tab_integration_tests.rs | 167 +++++- zellij-server/src/tab/unit/tab_tests.rs | 6 +- zellij-server/src/unit/screen_tests.rs | 4 +- zellij-utils/assets/layouts/test-deleteme.kdl | 178 ++++++ zellij-utils/src/input/actions.rs | 4 +- zellij-utils/src/input/layout.rs | 530 +++++++++--------- zellij-utils/src/input/unit/layout_test.rs | 434 ++++++-------- ...n_not_as_first_child_of_pane_template.snap | 265 ++++----- ...en_not_as_first_child_of_tab_template.snap | 284 ++++------ ..._and_pane_template_both_with_children.snap | 402 +++++-------- ...t_with_nested_branched_pane_templates.snap | 223 +++----- ...st__layout_with_nested_pane_templates.snap | 173 +++--- ...yout_test__layout_with_pane_templates.snap | 239 ++++++++ ...t__layout_with_tab_and_pane_templates.snap | 188 +++---- zellij-utils/src/kdl/kdl_layout_parser.rs | 29 +- zellij-utils/src/pane_size.rs | 15 +- ...i_arguments_override_layout_options-2.snap | 38 +- ...efault_config_with_no_cli_arguments-2.snap | 145 ++--- ...out_options_override_config_options-2.snap | 38 +- 25 files changed, 1819 insertions(+), 1643 deletions(-) create mode 100644 zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_basic_layout.snap create mode 100644 zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_layout.snap create mode 100644 zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_uneven_layout.snap create mode 100644 zellij-utils/assets/layouts/test-deleteme.kdl create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 0fc2c7a2b1..3617564f56 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -11,7 +11,7 @@ use zellij_utils::{ errors::{ContextType, PtyContext}, input::{ command::{RunCommand, TerminalAction}, - layout::{Layout, Run}, + layout::{PaneLayout, Layout, Run}, }, }; @@ -34,7 +34,7 @@ pub(crate) enum PtyInstruction { UpdateActivePane(Option, ClientId), GoToTab(TabIndex, ClientId), // NewTab(Option, Option, ClientId), - NewTab(Option, Option, Option, ClientId), // the String is the tab name + NewTab(Option, Option, Option, ClientId), // the String is the tab name ClosePane(PaneId), CloseTab(Vec), Exit, @@ -275,7 +275,7 @@ impl Pty { } pub fn spawn_terminals_for_layout( &mut self, - layout: Layout, + layout: PaneLayout, default_shell: Option, client_id: ClientId, ) { diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index deba6eb9c8..518d4d7263 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -8,7 +8,7 @@ use std::str; use zellij_utils::input::options::Clipboard; use zellij_utils::pane_size::{Size, SizeInPixels}; -use zellij_utils::{input::command::TerminalAction, input::layout::Layout, position::Position}; +use zellij_utils::{input::command::TerminalAction, input::layout::{PaneLayout, Layout}, position::Position}; use crate::panes::alacritty_functions::xparse_color; use crate::panes::terminal_character::AnsiCode; @@ -106,7 +106,7 @@ pub enum ScreenInstruction { ClosePane(PaneId, Option), UpdatePaneName(Vec, ClientId), UndoRenamePane(ClientId), - NewTab(Layout, Vec, ClientId), + NewTab(PaneLayout, Vec, ClientId), SwitchTabNext(ClientId), SwitchTabPrev(ClientId), ToggleActiveSyncTab(ClientId), @@ -648,7 +648,7 @@ impl Screen { /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. - pub fn new_tab(&mut self, layout: Layout, new_pids: Vec, client_id: ClientId) { + pub fn new_tab(&mut self, layout: PaneLayout, new_pids: Vec, client_id: ClientId) { let tab_index = self.get_new_tab_index(); let position = self.tabs.len(); let mut tab = Tab::new( diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index f7f64cff92..95ecd71115 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -40,7 +40,7 @@ use zellij_utils::{ data::{Event, InputMode, ModeInfo, Palette, PaletteColor, Style}, input::{ command::TerminalAction, - layout::{Layout, Run}, + layout::{PaneLayout, Layout, Run}, parse_keys, }, pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, @@ -450,7 +450,7 @@ impl Tab { pub fn apply_layout( &mut self, - layout: Layout, + layout: PaneLayout, new_pids: Vec, tab_index: usize, client_id: ClientId, @@ -474,7 +474,7 @@ impl Tab { let mut new_pids = new_pids.iter(); let mut focus_pane_id: Option = None; - let mut set_focus_pane_id = |layout: &Layout, pane_id: PaneId| { + let mut set_focus_pane_id = |layout: &PaneLayout, pane_id: PaneId| { if layout.focus.unwrap_or(false) && focus_pane_id.is_none() { focus_pane_id = Some(pane_id); } @@ -494,7 +494,7 @@ impl Tab { *position_and_size, self.senders.to_plugin.as_ref().unwrap().clone(), pane_title, - layout.pane_name.clone().unwrap_or_default(), + layout.name.clone().unwrap_or_default(), ); new_plugin.set_borderless(layout.borderless); self.tiled_panes @@ -509,7 +509,7 @@ impl Tab { *position_and_size, self.style, next_terminal_position, - layout.pane_name.clone().unwrap_or_default(), + layout.name.clone().unwrap_or_default(), self.link_handler.clone(), self.character_cell_size.clone(), self.sixel_image_store.clone(), diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_basic_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_basic_layout.snap new file mode 100644 index 0000000000..2a3dc369ad --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_basic_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 2130 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ │└──────────────────────────────────────────────────────────┘ +10 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_layout.snap new file mode 100644 index 0000000000..2d185efe2e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 2170 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────┐┌ Pane #2 ─────────────────────────────┐┌ Pane #6 ─────────────────────────────┐ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ │└──────────────────────────────────────┘│ │ +10 (C): │ │┌ Pane #3 ───┐┌ Pane #4 ──┐┌ Pane #5 ──┐│ │ +11 (C): │ ││ ││ ││ ││ │ +12 (C): │ ││ ││ ││ ││ │ +13 (C): │ ││ ││ ││ ││ │ +14 (C): │ ││ ││ ││ ││ │ +15 (C): │ ││ ││ ││ ││ │ +16 (C): │ ││ ││ ││ ││ │ +17 (C): │ ││ ││ ││ ││ │ +18 (C): │ ││ ││ ││ ││ │ +19 (C): └───────────────────────────────────────┘└────────────┘└───────────┘└───────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_uneven_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_uneven_layout.snap new file mode 100644 index 0000000000..6ac556ba9e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_uneven_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 2204 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ │└──────────────────────────────────────────────────────────┘ +05 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #4 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 35337b44c0..c7bb9f8ff7 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -11,7 +11,7 @@ use crate::{ }; use std::path::PathBuf; use zellij_utils::envs::set_session_name; -use zellij_utils::input::layout::Layout; +use zellij_utils::input::layout::{Layout, PaneLayout}; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::position::Position; @@ -144,7 +144,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { terminal_emulator_color_codes, ); tab.apply_layout( - Layout::with_one_pane(), + PaneLayout::default(), vec![1], index, client_id, @@ -152,6 +152,61 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { tab } +fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) -> Tab { + set_session_name("test".into()); + let index = 0; + let position = 0; + let name = String::new(); + let os_api = Box::new(FakeInputOutput { + file_dumps: Arc::new(Mutex::new(HashMap::new())), + }); + let senders = ThreadSenders::default().silently_fail_on_send(); + let max_panes = None; + let mode_info = default_mode; + let style = Style::default(); + let draw_pane_frames = true; + let client_id = 1; + let session_is_mirrored = true; + let mut connected_clients = HashSet::new(); + connected_clients.insert(client_id); + let connected_clients = Rc::new(RefCell::new(connected_clients)); + let character_cell_info = Rc::new(RefCell::new(None)); + let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); + let copy_options = CopyOptions::default(); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let layout = Layout::from_str(layout).unwrap(); + let tab_layout = layout.new_tab(); + let mut tab = Tab::new( + index, + position, + name, + size, + character_cell_info, + sixel_image_store, + os_api, + senders, + max_panes, + style, + mode_info, + draw_pane_frames, + connected_clients, + session_is_mirrored, + client_id, + copy_options, + terminal_emulator_colors, + terminal_emulator_color_codes, + ); + let pane_ids = tab_layout.extract_run_instructions().iter().enumerate().map(|(i, _)| i as i32).collect(); + tab.apply_layout( + tab_layout, + pane_ids, + index, + client_id, + ); + tab +} + fn create_new_tab_with_mock_pty_writer( size: Size, default_mode: ModeInfo, @@ -202,7 +257,7 @@ fn create_new_tab_with_mock_pty_writer( ); tab.apply_layout( // LayoutTemplate::default().try_into().unwrap(), - Layout::default(), + PaneLayout::default(), vec![1], index, client_id, @@ -261,7 +316,7 @@ fn create_new_tab_with_sixel_support( terminal_emulator_color_codes, ); tab.apply_layout( - Layout::with_one_pane(), + PaneLayout::default(), vec![1], index, client_id, @@ -2044,3 +2099,107 @@ fn pane_in_utf8_normal_event_tracking_mouse_mode() { ] ); } + +#[test] +fn tab_with_basic_layout() { + let layout = r#" + layout { + pane split_direction="Vertical" { + pane + pane split_direction="Horizontal" { + pane + pane + } + } + } + "#; + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); + let mut output = Output::default(); + tab.render(&mut output, None); + let snapshot = take_snapshot( + output.serialize().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn tab_with_nested_layout() { + let layout = r#" + layout { + pane_template name="top-and-vertical-sandwich" { + pane + vertical-sandwich { + pane + } + } + pane_template name="vertical-sandwich" split_direction="vertical" { + pane + children + pane + } + pane_template name="nested-vertical-sandwich" split_direction="vertical" { + pane + top-and-vertical-sandwich + pane + } + nested-vertical-sandwich + } + "#; + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); + let mut output = Output::default(); + tab.render(&mut output, None); + let snapshot = take_snapshot( + output.serialize().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn tab_with_nested_uneven_layout() { + let layout = r#" + layout { + pane_template name="horizontal-with-vertical-top" { + pane split_direction="Vertical" { + pane + children + } + pane + } + horizontal-with-vertical-top name="my tab" { + pane + pane + } + } + "#; + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); + let mut output = Output::default(); + tab.render(&mut output, None); + let snapshot = take_snapshot( + output.serialize().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 2452d52fce..b9ed281852 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -8,7 +8,7 @@ use crate::{ ClientId, }; use std::path::PathBuf; -use zellij_utils::input::layout::Layout; +use zellij_utils::input::layout::PaneLayout; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -128,7 +128,7 @@ fn create_new_tab(size: Size) -> Tab { terminal_emulator_color_codes, ); tab.apply_layout( - Layout::with_one_pane(), + PaneLayout::default(), vec![1], index, client_id, @@ -179,7 +179,7 @@ fn create_new_tab_with_cell_size( terminal_emulator_color_codes, ); tab.apply_layout( - Layout::with_one_pane(), + PaneLayout::default(), vec![1], index, client_id, diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 3e0e6549ba..307acec4e8 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -8,7 +8,7 @@ use crate::{ use std::convert::TryInto; use std::path::PathBuf; use zellij_utils::input::command::TerminalAction; -use zellij_utils::input::layout::Layout; +use zellij_utils::input::layout::{PaneLayout, Layout}; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -111,7 +111,7 @@ fn create_new_screen(size: Size) -> Screen { fn new_tab(screen: &mut Screen, pid: i32) { let client_id = 1; screen.new_tab( - Layout::with_one_pane(), + PaneLayout::default(), vec![pid], client_id, ); diff --git a/zellij-utils/assets/layouts/test-deleteme.kdl b/zellij-utils/assets/layouts/test-deleteme.kdl new file mode 100644 index 0000000000..556e2ba686 --- /dev/null +++ b/zellij-utils/assets/layouts/test-deleteme.kdl @@ -0,0 +1,178 @@ + +// layout { +// pane_template name="htop" { +// pane command="htop" +// vertical-sandwich { +// pane +// } +// } +// pane_template name="vertical-sandwich" split_direction="vertical" { +// pane +// children +// pane +// } +// pane_template name="htop-vertical-sandwich" split_direction="vertical" { +// pane +// htop +// pane +// } +// htop-vertical-sandwich +// } + +// layout { +// pane_template name="htop" { +// pane command="htop" +// vertical-sandwich +// } +// pane_template name="vertical-sandwich" split_direction="vertical" { +// pane +// children +// } +// pane_template name="htop-vertical-sandwich" split_direction="vertical" { +// pane +// htop +// pane +// } +// htop-vertical-sandwich +// } +// +// layout { +// pane_template name="htop" { +// pane command="htop" +// vertical-sandwich +// } +// pane_template name="vertical-sandwich" split_direction="vertical" { +// pane +// // children +// pane +// pane +// } +// pane_template name="htop-vertical-sandwich" split_direction="vertical" { +// pane +// htop +// pane +// } +// htop-vertical-sandwich +// } +// layout { +// tab_template name="horizontal-with-vertical-top" { +// pane split_direction="Vertical" { +// pane name="1" +// // pane name="3" +// pane { +// pane name="3" +// } +// pane name="4" +// } +// pane name="2" +// } +// // horizontal-with-vertical-top name="my tab" split_direction="Horizontal" { +// // pane +// // pane +// // } +// horizontal-with-vertical-top +// // horizontal-with-vertical-top split_direction="Horizontal" { +// // pane +// // } +// } +// layout { +// pane_template name="htop" { +// pane command="htop" +// vertical-sandwich { +// pane +// } +// } +// pane_template name="vertical-sandwich" split_direction="vertical" { +// pane +// children +// pane +// } +// pane_template name="htop-vertical-sandwich" split_direction="vertical" { +// pane +// htop +// pane +// } +// htop-vertical-sandwich +// } +// layout { +// pane size=1 borderless=true { +// plugin location="zellij:tab-bar" +// } +// pane +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } +// } + +// layout { +// tab_template name="horizontal-with-vertical-top" { +// pane split_direction="Vertical" { +// pane name="3" +// children +// } +// pane name="4" +// } +// horizontal-with-vertical-top name="my tab" { +// pane name="1" +// pane name="2" +// } +// horizontal-with-vertical-top +// } + + +// layout { +// pane_template name="horizontal-with-vertical-top" { +// pane split_direction="Vertical" { +// pane +// children +// } +// pane +// } +// horizontal-with-vertical-top name="my tab" { +// pane +// pane +// } +// horizontal-with-vertical-top +// } +// layout { +// tab_template name="horizontal-with-vertical-top" { +// pane split_direction="Vertical" { +// pane +// children +// } +// pane +// } +// horizontal-with-vertical-top name="my tab" { +// pane +// pane +// } +// horizontal-with-vertical-top +// } +// layout { +// tab_template name="horizontal-with-vertical-top" { +// vertical-sandwich { +// pane name="middle" +// } +// children +// } +// pane_template name="vertical-sandwich" split_direction="Vertical" { +// pane +// children +// pane +// } +// horizontal-with-vertical-top name="my tab" { +// pane +// pane +// } +// horizontal-with-vertical-top +// } + layout { + tab name="my cool tab name 1" + tab name="my cool tab name 2" + default_tab_template { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane + } + } diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 05a47be517..6baa77c6c2 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,7 +1,7 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::Layout; +use super::layout::{PaneLayout, Layout}; use crate::data::InputMode; use crate::input::options::OnForceClose; use serde::{Deserialize, Serialize}; @@ -157,7 +157,7 @@ pub enum Action { PaneNameInput(Vec), UndoRenamePane, /// Create a new tab, optionally with a specified tab layout. - NewTab(Option, Option), // the String is the tab name + NewTab(Option, Option), // the String is the tab name /// Do nothing. NoOp, /// Go to the next tab. diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 7698938370..e37292777d 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -173,11 +173,33 @@ impl PaneLayout { if self.external_children_index.is_some() { count += 1; } - for pane in self.children { + for pane in &self.children { count += pane.children_block_count(); } count } + pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { + // split_space(space, self, space) + let res = split_space(space, self, space); + res + } + pub fn extract_run_instructions(&self) -> Vec> { + let mut run_instructions = vec![]; + if self.children.is_empty() { + run_instructions.push(self.run.clone()); + } + for child in &self.children { + let mut child_run_instructions = child.extract_run_instructions(); + run_instructions.append(&mut child_run_instructions); + } + run_instructions + } + pub fn with_one_pane() -> Self { + // TODO: do we need this? + let mut default_layout = PaneLayout::default(); + default_layout.children = vec![PaneLayout::default()]; + default_layout + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -217,11 +239,6 @@ impl Default for LayoutParts { } impl Layout { - pub fn with_one_pane() -> Self { - let mut default_layout = Layout::default(); - default_layout.parts = LayoutParts::Panes(vec![Layout::default()]); - default_layout - } pub fn stringified_from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result { match layout_path { Some(layout_path) => { @@ -251,6 +268,10 @@ impl Layout { let config = Config::from_kdl(&raw_layout, Some(config))?; // this merges the two config, with Ok((layout, config)) } + pub fn from_str(raw: &str) -> Result { + let kdl_layout: KdlDocument = raw.parse()?; + Layout::from_kdl(&kdl_layout) + } pub fn stringified_from_dir( layout: &PathBuf, layout_dir: Option<&PathBuf>, @@ -307,275 +328,284 @@ impl Layout { Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?) } - pub fn total_terminal_panes(&self) -> usize { - // TODO: better - let mut total_panes = 0; - match &self.parts { - LayoutParts::Panes(parts) => { - total_panes += parts.len(); - for part in parts { - match part.run { - Some(Run::Command(_)) | None => { - total_panes += part.total_terminal_panes(); - }, - Some(Run::Plugin(_)) => {}, - } - } - total_panes - }, - LayoutParts::Tabs(tabs) => { - // let parts = tabs.values(); - total_panes += tabs.len(); - for tab in tabs { - let (_tab_name, part) = tab; - match part.run { - Some(Run::Command(_)) | None => { - total_panes += part.total_terminal_panes(); - }, - Some(Run::Plugin(_)) => {}, - } - } - total_panes - } - } - } - - pub fn total_borderless_panes(&self) -> usize { - // TODO: better - let mut total_borderless_panes = 0; - match &self.parts { - LayoutParts::Panes(parts) => { - total_borderless_panes += parts.iter().filter(|p| p.borderless).count(); - for part in parts { - total_borderless_panes += part.total_borderless_panes(); - } - total_borderless_panes - }, - LayoutParts::Tabs(tabs) => { - total_borderless_panes += tabs.iter().filter(|(_, p)| p.borderless).count(); - for part in tabs { - let (_part_name, part) = part; - total_borderless_panes += part.total_borderless_panes(); - } - total_borderless_panes - } - } - } - pub fn extract_run_instructions(&self) -> Vec> { - // TODO: better - let mut run_instructions = vec![]; - match &self.parts { - LayoutParts::Panes(parts) => { - if parts.is_empty() { - run_instructions.push(self.run.clone()); - } - for part in parts { - let mut current_runnables = part.extract_run_instructions(); - run_instructions.append(&mut current_runnables); - } - }, - LayoutParts::Tabs(tabs) => { - if tabs.len() == 0 { - run_instructions.push(self.run.clone()); - } - for tab in tabs { - let (_part_name, part) = tab; - let mut current_runnables = part.extract_run_instructions(); - run_instructions.append(&mut current_runnables); - } - } - } - run_instructions - } - - pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(Layout, PaneGeom)> { - split_space(space, self) - } - - pub fn new_tab(&self) -> Layout { +// pub fn total_terminal_panes(&self) -> usize { +// // TODO: better +// let mut total_panes = 0; +// match &self.parts { +// LayoutParts::Panes(parts) => { +// total_panes += parts.len(); +// for part in parts { +// match part.run { +// Some(Run::Command(_)) | None => { +// total_panes += part.total_terminal_panes(); +// }, +// Some(Run::Plugin(_)) => {}, +// } +// } +// total_panes +// }, +// LayoutParts::Tabs(tabs) => { +// // let parts = tabs.values(); +// total_panes += tabs.len(); +// for tab in tabs { +// let (_tab_name, part) = tab; +// match part.run { +// Some(Run::Command(_)) | None => { +// total_panes += part.total_terminal_panes(); +// }, +// Some(Run::Plugin(_)) => {}, +// } +// } +// total_panes +// } +// } +// } + +// pub fn total_borderless_panes(&self) -> usize { +// // TODO: better +// let mut total_borderless_panes = 0; +// match &self.parts { +// LayoutParts::Panes(parts) => { +// total_borderless_panes += parts.iter().filter(|p| p.borderless).count(); +// for part in parts { +// total_borderless_panes += part.total_borderless_panes(); +// } +// total_borderless_panes +// }, +// LayoutParts::Tabs(tabs) => { +// total_borderless_panes += tabs.iter().filter(|(_, p)| p.borderless).count(); +// for part in tabs { +// let (_part_name, part) = part; +// total_borderless_panes += part.total_borderless_panes(); +// } +// total_borderless_panes +// } +// } +// } +// pub fn extract_run_instructions(&self) -> Vec> { +// // TODO: better +// let mut run_instructions = vec![]; +// match &self.parts { +// LayoutParts::Panes(parts) => { +// if parts.is_empty() { +// run_instructions.push(self.run.clone()); +// } +// for part in parts { +// let mut current_runnables = part.extract_run_instructions(); +// run_instructions.append(&mut current_runnables); +// } +// }, +// LayoutParts::Tabs(tabs) => { +// if tabs.len() == 0 { +// run_instructions.push(self.run.clone()); +// } +// for tab in tabs { +// let (_part_name, part) = tab; +// let mut current_runnables = part.extract_run_instructions(); +// run_instructions.append(&mut current_runnables); +// } +// } +// } +// run_instructions +// } + +// pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { +// split_space(space, self) +// } + + pub fn new_tab(&self) -> PaneLayout { match &self.template { - Some(template) => *template.clone(), - None => Layout::with_one_pane() + Some(template) => template.clone(), + None => PaneLayout::default() } } pub fn is_empty(&self) -> bool { - match &self.parts { - LayoutParts::Tabs(tabs) => tabs.is_empty(), - LayoutParts::Panes(panes) => panes.is_empty(), - } + !self.tabs.is_empty() +// match &self.parts { +// LayoutParts::Tabs(tabs) => tabs.is_empty(), +// LayoutParts::Panes(panes) => panes.is_empty(), +// } } + // TODO: do we need both of these? pub fn has_tabs(&self) -> bool { - match self.parts { - LayoutParts::Tabs(_) => true, - _ => false - } + !self.tabs.is_empty() } - pub fn tabs(&self) -> Vec<(Option, Layout)> { // String is the tab name - match &self.parts { - LayoutParts::Tabs(tabs) => tabs.clone(), - _ => vec![] - } + pub fn tabs(&self) -> Vec<(Option, PaneLayout)> { // String is the tab name + self.tabs.clone() +// match &self.parts { +// LayoutParts::Tabs(tabs) => tabs.clone(), +// _ => vec![] +// } } pub fn focused_tab_index(&self) -> Option { self.focused_tab_index } - pub fn children_block_count(&self) -> usize { - let mut count = 0; - if self.external_children_index.is_some() { - count += 1; - } - match &self.parts { - LayoutParts::Tabs(tabs) => { - for tab in tabs { - count += tab.1.children_block_count(); - } - } - LayoutParts::Panes(panes) => { - for pane in panes { - count += pane.children_block_count(); - } - } - } - count - } - pub fn insert_children_layout(&mut self, children_layout: &mut Layout) -> Result { - // returns true if successfully inserted and false otherwise - let external_children_index = self.external_children_index; - match &mut self.parts { - LayoutParts::Tabs(tabs) => Err(ConfigError::KdlParsingError("Cannot insert child layout in tabs".into())), - LayoutParts::Panes(panes) => { - match external_children_index { - Some(external_children_index) => { - panes.insert(external_children_index, children_layout.clone()); - self.external_children_index = None; - Ok(true) - }, - None => { - for pane in panes.iter_mut() { - if pane.insert_children_layout(children_layout)? { - return Ok(true); - } - } - Ok(false) - } - } - } - } - } +// pub fn children_block_count(&self) -> usize { +// let mut count = 0; +// if self.external_children_index.is_some() { +// count += 1; +// } +// match &self.parts { +// LayoutParts::Tabs(tabs) => { +// for tab in tabs { +// count += tab.1.children_block_count(); +// } +// } +// LayoutParts::Panes(panes) => { +// for pane in panes { +// count += pane.children_block_count(); +// } +// } +// } +// count +// } +// pub fn insert_children_layout(&mut self, children_layout: &mut Layout) -> Result { +// // returns true if successfully inserted and false otherwise +// let external_children_index = self.external_children_index; +// match &mut self.parts { +// LayoutParts::Tabs(tabs) => Err(ConfigError::KdlParsingError("Cannot insert child layout in tabs".into())), +// LayoutParts::Panes(panes) => { +// match external_children_index { +// Some(external_children_index) => { +// panes.insert(external_children_index, children_layout.clone()); +// self.external_children_index = None; +// Ok(true) +// }, +// None => { +// for pane in panes.iter_mut() { +// if pane.insert_children_layout(children_layout)? { +// return Ok(true); +// } +// } +// Ok(false) +// } +// } +// } +// } +// } } -fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { - fn child_layout_size( - direction: SplitDirection, - parent_direction: SplitDirection, - layout: &Layout, - ) -> usize { - let size = if parent_direction == direction { 1 } else { 0 }; - let parts_is_empty = match &layout.parts { - LayoutParts::Panes(parts) => parts.is_empty(), - LayoutParts::Tabs(tabs) => tabs.len() == 0 +// fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { +// fn child_layout_size( +// direction: SplitDirection, +// parent_direction: SplitDirection, +// layout: &Layout, +// ) -> usize { +// let size = if parent_direction == direction { 1 } else { 0 }; +// let parts_is_empty = match &layout.parts { +// LayoutParts::Panes(parts) => parts.is_empty(), +// LayoutParts::Tabs(tabs) => tabs.len() == 0 +// }; +// // if layout.parts.is_empty() { +// if parts_is_empty { +// size +// } else { +// match &layout.parts { +// LayoutParts::Panes(parts) => { +// let children_size = parts +// .iter() +// .map(|p| child_layout_size(direction, layout.direction, p)) +// .sum(); +// max(size, children_size) +// }, +// LayoutParts::Tabs(tabs) => { +// let children_size = tabs +// .iter() +// .map(|(_, p)| child_layout_size(direction, layout.direction, p)) +// .sum(); +// max(size, children_size) +// } +// } +// } +// } +// child_layout_size(direction, direction, layout) +// } + +fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_split: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { + let mut pane_positions = Vec::new(); + let sizes: Vec> = layout.children.iter().map(|part| part.split_size).collect(); + + let mut split_geom = Vec::new(); + let (mut current_position, split_dimension_space, mut inherited_dimension, total_split_dimension_space) = + match layout.children_split_direction { + SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows, total_space_to_split.cols), + SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols, total_space_to_split.rows), }; - // if layout.parts.is_empty() { - if parts_is_empty { - size - } else { - match &layout.parts { - LayoutParts::Panes(parts) => { - let children_size = parts - .iter() - .map(|p| child_layout_size(direction, layout.direction, p)) - .sum(); - max(size, children_size) - }, - LayoutParts::Tabs(tabs) => { - let children_size = tabs - .iter() - .map(|(_, p)| child_layout_size(direction, layout.direction, p)) - .sum(); - max(size, children_size) - } - } - } - } - child_layout_size(direction, direction, layout) -} -fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneGeom)> { - match &layout.parts { - LayoutParts::Panes(parts) => { - let mut pane_positions = Vec::new(); - let sizes: Vec> = parts.iter().map(|part| part.split_size).collect(); - - let mut split_geom = Vec::new(); - let (mut current_position, split_dimension_space, mut inherited_dimension) = - match layout.direction { - SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), - SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), - }; + let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); - let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); - - for (&size, part) in sizes.iter().zip(&*parts) { - let mut split_dimension = match size { - Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), - Some(SplitSize::Fixed(size)) => Dimension::fixed(size), - None => { - let total_fixed_size = split_dimension_space.as_usize(); - let free_percent = if let Some(p) = split_dimension_space.as_percent() { - p - sizes - .iter() - .map(|&s| { - match s { - Some(SplitSize::Percent(ip)) => ip as f64, - Some(SplitSize::Fixed(fixed)) => (fixed as f64 / total_fixed_size as f64) * 100.0, - _ => 0.0, - } - }) - .sum::() - } else { - panic!("Implicit sizing within fixed-size panes is not supported"); - }; - Dimension::percent(free_percent / flex_parts as f64) - }, - }; - split_dimension.adjust_inner(split_dimension_space.as_usize()); - let geom = match layout.direction { - SplitDirection::Vertical => PaneGeom { - x: current_position, - y: space_to_split.y, - cols: split_dimension, - rows: inherited_dimension, - }, - SplitDirection::Horizontal => PaneGeom { - x: space_to_split.x, - y: current_position, - cols: inherited_dimension, - rows: split_dimension, - }, + let mut total_pane_size = 0; + for (&size, part) in sizes.iter().zip(&*layout.children) { + let mut split_dimension = match size { + Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), + Some(SplitSize::Fixed(size)) => Dimension::fixed(size), + None => { + let total_fixed_size = split_dimension_space.as_usize(); + let free_percent = if let Some(p) = split_dimension_space.as_percent() { + p - sizes + .iter() + .map(|&s| { + match s { + Some(SplitSize::Percent(ip)) => ip as f64, + Some(SplitSize::Fixed(fixed)) => (fixed as f64 / total_fixed_size as f64) * 100.0, + _ => 0.0, + } + }) + .sum::() + } else { + panic!("Implicit sizing within fixed-size panes is not supported"); }; - split_geom.push(geom); - current_position += split_dimension.as_usize(); - } + Dimension::percent(free_percent / flex_parts as f64) + }, + }; + split_dimension.adjust_inner(total_split_dimension_space.as_usize()); + total_pane_size += split_dimension.as_usize(); + + let geom = match layout.children_split_direction { + SplitDirection::Vertical => PaneGeom { + x: current_position, + y: space_to_split.y, + cols: split_dimension, + rows: inherited_dimension, + }, + SplitDirection::Horizontal => PaneGeom { + x: space_to_split.x, + y: current_position, + cols: inherited_dimension, + rows: split_dimension, + }, + }; + split_geom.push(geom); + current_position += split_dimension.as_usize(); + } - for (i, part) in parts.iter().enumerate() { - let part_position_and_size = split_geom.get(i).unwrap(); - if !part.parts.is_empty() { - let mut part_positions = split_space(part_position_and_size, part); - pane_positions.append(&mut part_positions); - } else { - pane_positions.push((part.clone(), *part_position_and_size)); - } + // add extra space from rounding errors to the last pane + if total_pane_size < split_dimension_space.as_usize() { + let increase_by = split_dimension_space.as_usize() - total_pane_size; + if let Some(last_geom) = split_geom.last_mut() { + match layout.children_split_direction { + SplitDirection::Vertical => last_geom.cols.increase_inner(increase_by), + SplitDirection::Horizontal => last_geom.rows.increase_inner(increase_by), } - pane_positions - }, - LayoutParts::Tabs(tabs) => { - panic!("tab layout should not have nested tabs"); } } + for (i, part) in layout.children.iter().enumerate() { + let part_position_and_size = split_geom.get(i).unwrap(); + if !part.children.is_empty() { + let mut part_positions = split_space(part_position_and_size, part, total_space_to_split); + pane_positions.append(&mut part_positions); + } else { + pane_positions.push((part.clone(), *part_position_and_size)); + } + } + if pane_positions.is_empty() { + pane_positions.push((layout.clone(), space_to_split.clone())); + } + pane_positions } impl TryFrom for RunPluginLocation { diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 8db1636990..66868ec21f 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -3,9 +3,6 @@ use super::super::layout::*; use std::convert::TryInto; use insta::assert_snapshot; -// println!("layout: {:#?}", layout); -// println!("expected_layout: {:#?}", expected_layout); - fn layout_test_dir(layout: String) -> PathBuf { let root = Path::new(env!("CARGO_MANIFEST_DIR")); let layout_dir = root.join("src/input/unit/fixtures/layouts"); @@ -24,7 +21,7 @@ fn empty_layout() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout::with_one_pane())), + template: Some(PaneLayout::default()), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -40,7 +37,10 @@ fn layout_with_one_pane() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout::with_one_pane())), + template: Some(PaneLayout { + children: vec![PaneLayout::default()], + ..Default::default() + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -58,14 +58,14 @@ fn layout_with_multiple_panes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), + template: Some(PaneLayout { + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -88,26 +88,26 @@ fn layout_with_nested_panes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + template: Some(PaneLayout { + children: vec![ + PaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }, - Layout { - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + PaneLayout { + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() } - ]), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -123,10 +123,10 @@ fn layout_with_tabs() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - parts: LayoutParts::Tabs(vec![ - (None, Layout::with_one_pane()), - ]), - template: Some(Box::new(Layout::with_one_pane())), + tabs: vec![ + (None, PaneLayout::default()), + ], + template: Some(PaneLayout::default()), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -150,26 +150,26 @@ fn layout_with_nested_differing_tabs() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - parts: LayoutParts::Tabs(vec![ - (None, Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), + tabs: vec![ + (None, PaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }), - (None, Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + (None, PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }), - ]), - template: Some(Box::new(Layout::with_one_pane())), + ], + template: Some(PaneLayout::default()), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -188,27 +188,27 @@ fn layout_with_panes_in_different_mixed_split_sizes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout { + template: Some(PaneLayout { + children: vec![ + PaneLayout { split_size: Some(SplitSize::Fixed(1)), ..Default::default() }, - Layout { + PaneLayout { split_size: Some(SplitSize::Percent(10)), ..Default::default() }, - Layout { + PaneLayout { split_size: None, ..Default::default() }, - Layout { + PaneLayout { split_size: Some(SplitSize::Fixed(2)), ..Default::default() }, - ]), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -224,20 +224,18 @@ fn layout_with_command_panes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new( - Layout { - parts: LayoutParts::Panes(vec![ - Layout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - ..Default::default() - })), + template: Some(PaneLayout { + children: vec![ + PaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), ..Default::default() - } - ]), - ..Default::default() - }, - )), + })), + ..Default::default() + } + ], + ..Default::default() + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -253,10 +251,10 @@ fn layout_with_command_panes_and_cwd() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new( - Layout { - parts: LayoutParts::Panes(vec![ - Layout { + template: Some( + PaneLayout { + children: vec![ + PaneLayout { run: Some(Run::Command(RunCommand { command: PathBuf::from("htop"), cwd: Some(PathBuf::from("/path/to/my/cwd")), @@ -264,10 +262,10 @@ fn layout_with_command_panes_and_cwd() { })), ..Default::default() } - ]), + ], ..Default::default() }, - )), + ), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -285,10 +283,10 @@ fn layout_with_command_panes_and_cwd_and_args() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new( - Layout { - parts: LayoutParts::Panes(vec![ - Layout { + template: Some( + PaneLayout { + children: vec![ + PaneLayout { run: Some(Run::Command(RunCommand { command: PathBuf::from("htop"), cwd: Some(PathBuf::from("/path/to/my/cwd")), @@ -297,10 +295,10 @@ fn layout_with_command_panes_and_cwd_and_args() { })), ..Default::default() } - ]), + ], ..Default::default() }, - )), + ), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -321,25 +319,25 @@ fn layout_with_plugin_panes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout { + template: Some(PaneLayout { + children: vec![ + PaneLayout { run: Some(Run::Plugin(RunPlugin { location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), _allow_exec_host_cmd: false, })), ..Default::default() }, - Layout { + PaneLayout { run: Some(Run::Plugin(RunPlugin { location: RunPluginLocation::File(PathBuf::from("/path/to/my/plugin.wasm")), _allow_exec_host_cmd: false, })), ..Default::default() }, - ]), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -355,15 +353,15 @@ fn layout_with_borderless_panes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout { + template: Some(PaneLayout { + children: vec![ + PaneLayout { borderless: true, ..Default::default() }, - ]), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -379,15 +377,15 @@ fn layout_with_focused_panes() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout { + template: Some(PaneLayout { + children: vec![ + PaneLayout { focus: Some(true), ..Default::default() }, - ]), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -403,15 +401,15 @@ fn layout_with_pane_names() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - template: Some(Box::new(Layout { - parts: LayoutParts::Panes(vec![ - Layout { - pane_name: Some("my awesome pane".into()), + template: Some(PaneLayout { + children: vec![ + PaneLayout { + name: Some("my awesome pane".into()), ..Default::default() }, - ]), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -428,22 +426,17 @@ fn layout_with_tab_names() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Tabs(vec![ - (Some("my cool tab name 1".into()), Layout { - parts: LayoutParts::Panes(vec![ - Layout::default() - ]), + tabs: vec![ + (Some("my cool tab name 1".into()), PaneLayout { + children: vec![], ..Default::default() }), - (Some("my cool tab name 2".into()), Layout { - parts: LayoutParts::Panes(vec![ - Layout::default() - ]), + (Some("my cool tab name 2".into()), PaneLayout { + children: vec![], ..Default::default() }), - ]), - template: Some(Box::new(Layout::with_one_pane())), + ], + template: Some(PaneLayout::default()), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -472,51 +465,50 @@ fn layout_with_tab_templates() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Tabs(vec![ - (Some("my first tab".into()), Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + tabs: vec![ + (Some("my first tab".into()), PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }, - Layout::default(), - ]), + PaneLayout::default(), + ], ..Default::default() }), - (Some("my second tab".into()), Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + (Some("my second tab".into()), PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }, - Layout::default(), - ]), + PaneLayout::default(), + ], ..Default::default() }), - (None, Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), + (None, PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }), - ]), - template: Some(Box::new(Layout::with_one_pane())), + ], + template: Some(PaneLayout::default()), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -545,59 +537,58 @@ fn layout_with_default_tab_template() { let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); let expected_layout = Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Tabs(vec![ - (Some("my first tab".into()), Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + tabs: vec![ + (Some("my first tab".into()), PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }, - Layout::default(), - ]), + PaneLayout::default(), + ], ..Default::default() }), - (Some("my second tab".into()), Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), + (Some("my second tab".into()), PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }, - Layout::default(), - ]), + PaneLayout::default(), + ], ..Default::default() }), - (None, Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), + (None, PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() }), - ]), - template: Some(Box::new(Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), + ], + template: Some(PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], ..Default::default() - })), + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -628,66 +619,7 @@ fn layout_with_pane_templates() { "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); - let expected_layout = Layout { - template: Some(Box::new(Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::with_one_pane(), - Layout::default(), - ]), - ..Default::default() - }, - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Horizontal, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), - ..Default::default() - }, - Layout::default(), - ]), - ..Default::default() - }, - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - ]), - ..Default::default() - }, - Layout::default(), - ]), - ..Default::default() - }, - Layout { - direction: SplitDirection::Vertical, - parts: LayoutParts::Panes(vec![ - Layout::default(), - Layout::default(), - Layout::default(), - ]), - ..Default::default() - } - ]), - ..Default::default() - })), - ..Default::default() - }; - assert_eq!(layout, expected_layout); + assert_snapshot!(format!("{:#?}", layout)); } #[test] diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap index b40692ca35..f82879d2b9 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap @@ -1,196 +1,139 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 856 +assertion_line: 848 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, + tabs: [], focused_tab_index: None, template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: Some( - 1, - ), - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 1, + ), + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap index 9c83214cdf..003a3d4115 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap @@ -1,219 +1,147 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 809 +assertion_line: 801 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Tabs( - [ - ( - Some( - "my tab", - ), - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + tabs: [ + ( + Some( + "my tab", + ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ), - ( - None, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: Some( - 1, - ), - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ), - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 1, ), + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap index 9e16111f2d..a964c655a1 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap @@ -1,293 +1,201 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 907 +assertion_line: 899 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Tabs( - [ - ( - Some( - "my tab", - ), - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: Some( - "middle", - ), - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + tabs: [ + ( + Some( + "my tab", + ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: Some( + "middle", + ), + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ), - ( - None, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: Some( - "middle", - ), - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ), - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: Some( + "middle", + ), + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap index da5b11eff2..77b7478c8c 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap @@ -1,178 +1,125 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 761 +assertion_line: 756 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, + tabs: [], focused_tab_index: None, template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap index 5e7d79d323..fd12e42b49 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap @@ -1,135 +1,94 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 734 +assertion_line: 729 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, + tabs: [], focused_tab_index: None, template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap new file mode 100644 index 0000000000..561c35f1c9 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap @@ -0,0 +1,239 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 626 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap index 5f958f4f52..72101338be 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap @@ -1,148 +1,96 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 712 +assertion_line: 648 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Tabs( - [ - ( - None, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Vertical, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: Some( - Command( - RunCommand { - command: "htop", - args: [], - cwd: None, - }, - ), - ), - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "htop", + args: [], + cwd: None, + }, ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ), - ], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ], - ), + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 5ba68dccf6..7cd543cfda 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -144,37 +144,34 @@ impl <'a>KdlLayoutParser <'a> { fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); - let pane_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|name| name.to_string()); + let name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|name| name.to_string()); let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_or_plugin_block(kdl_node)?; - let direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { + let children_split_direction = self.parse_split_direction(kdl_node)?; + let (external_children_index, children) = match kdl_children_nodes!(kdl_node) { Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, None => (None, vec![]) }; - Ok(Layout { + Ok(PaneLayout { borderless: borderless.unwrap_or_default(), focus, - pane_name, + name, split_size, run, - direction, + children_split_direction, external_children_index, - parts: LayoutParts::Panes(pane_parts), + children, ..Default::default() }) } fn parse_pane_node_with_template(&self, kdl_node: &KdlNode, mut pane_layout: PaneLayout) -> Result { - let direction = self.parse_split_direction(kdl_node)?; + let children_split_direction = self.parse_split_direction(kdl_node)?; match kdl_children_nodes!(kdl_node) { Some(children) => { - let (_, mut child_panes) = self.parse_child_pane_nodes_for_pane(&children)?; - if child_panes.is_empty() { - child_panes.push(Layout::default()); - } - let child_panes_layout = Layout { - direction, - parts: LayoutParts::Panes(child_panes), + let child_panes = self.parse_child_pane_nodes_for_tab(children)?; + let child_panes_layout = PaneLayout { + children_split_direction, + children: child_panes, ..Default::default() }; self.assert_one_children_block(&pane_layout)?; @@ -182,7 +179,7 @@ impl <'a>KdlLayoutParser <'a> { }, None => { if let Some(index_of_children) = pane_layout.external_children_index { - pane_layout.parts.insert_pane(index_of_children, Layout::default())?; + pane_layout.children.insert(index_of_children, PaneLayout::default()); } } } diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 789df446fd..3f6e0487e3 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -83,16 +83,27 @@ impl Dimension { self.inner = inner; } - pub fn adjust_inner(&mut self, full_size: usize) { + pub fn adjust_inner(&mut self, full_size: usize) -> f64 { // returns the leftover from + // rounding if any + // TODO: elsewhere? match self.constraint { Constraint::Percent(percent) => { - self.set_inner(((percent / 100.0) * full_size as f64) as usize); + let new_inner = (percent / 100.0) * full_size as f64; + let rounded = new_inner.floor(); + let leftover = rounded - new_inner; + self.set_inner(rounded as usize); + leftover + // self.set_inner(((percent / 100.0) * full_size as f64).round() as usize); } Constraint::Fixed(fixed_size) => { self.set_inner(fixed_size); + 0.0 } } } + pub fn increase_inner(&mut self, by: usize) { + self.inner += by; + } pub fn is_fixed(&self) -> bool { matches!(self.constraint, Constraint::Fixed(_)) diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap index 7e32b875ba..263f3c6087 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap @@ -4,46 +4,18 @@ assertion_line: 509 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, + tabs: [], focused_tab_index: None, template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap index 3a5328593c..4611ca2d53 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap @@ -4,104 +4,79 @@ assertion_line: 468 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, + tabs: [], focused_tab_index: None, template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: Some( - Fixed( - 1, - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, ), - run: Some( - Plugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "tab-bar", - ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", ), - }, - ), - ), - borderless: true, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], + ), + }, ), - split_size: Some( - Fixed( - 2, - ), + ), + borderless: true, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, ), - run: Some( - Plugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "status-bar", - ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", ), - }, - ), + ), + }, ), - borderless: true, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + ), + borderless: true, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap index 7b5cfc5a25..3a4e696dfc 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap @@ -4,46 +4,18 @@ assertion_line: 492 expression: "format!(\"{:#?}\", layout)" --- Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, + tabs: [], focused_tab_index: None, template: Some( - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [ - Layout { - direction: Horizontal, - pane_name: None, - parts: Panes( - [], - ), - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - focused_tab_index: None, - template: None, - }, - ], - ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, - focused_tab_index: None, - template: None, }, ), } From 44467ad26f7a850c8b5cc30662c53d5f03b15b61 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 7 Sep 2022 09:24:57 +0200 Subject: [PATCH 18/55] work --- zellij-server/src/panes/tiled_panes/mod.rs | 9 ++++++++- zellij-utils/assets/layouts/default.kdl | 8 ++++---- zellij-utils/src/input/layout.rs | 8 ++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index d684912b51..74f11bdfd9 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -155,6 +155,8 @@ impl TiledPanes { removed_pane } pub fn insert_pane(&mut self, pane_id: PaneId, mut pane: Box) { + log::info!(""); + log::info!("inserting pane"); let cursor_height_width_ratio = self.cursor_height_width_ratio(); let pane_grid = TiledPaneGrid::new( &mut self.panes, @@ -169,6 +171,7 @@ impl TiledPanes { let pane_to_split = self.panes.get_mut(&pane_id_to_split).unwrap(); let size_of_both_panes = pane_to_split.position_and_size(); if let Some((first_geom, second_geom)) = split(split_direction, &size_of_both_panes) { + log::info!("first_geom: {:?}, second_geom: {:?}", first_geom, second_geom); pane_to_split.set_geom(first_geom); pane.set_geom(second_geom); self.panes.insert(pane_id, pane); @@ -220,9 +223,13 @@ impl TiledPanes { ); let result = match direction { SplitDirection::Horizontal => { + log::info!("relayout horizontal"); pane_grid.layout(direction, (*self.display_area.borrow()).cols) }, - SplitDirection::Vertical => pane_grid.layout(direction, (*self.display_area.borrow()).rows), + SplitDirection::Vertical => { + log::info!("relayout vertical"); + pane_grid.layout(direction, (*self.display_area.borrow()).rows) + } }; if let Err(e) = &result { log::error!("{:?} relayout of the tab failed: {}", direction, e); diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index f8b4595d7b..9103b6e4d1 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -1,11 +1,11 @@ layout { pane size=1 borderless=true { - plugin location="zellij:tab-bar" +// plugin location="zellij:tab-bar" } pane - pane size=2 borderless=true { - plugin location="zellij:status-bar" - } +// pane size=2 borderless=true { +// plugin location="zellij:status-bar" +// } } // keybinds { // normal { diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index e37292777d..12cbeceac8 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -179,8 +179,8 @@ impl PaneLayout { count } pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { - // split_space(space, self, space) let res = split_space(space, self, space); + log::info!("res: {:#?}", res); res } pub fn extract_run_instructions(&self) -> Vec> { @@ -530,10 +530,10 @@ fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_sp let sizes: Vec> = layout.children.iter().map(|part| part.split_size).collect(); let mut split_geom = Vec::new(); - let (mut current_position, split_dimension_space, mut inherited_dimension, total_split_dimension_space) = + let (mut current_position, split_dimension_space, mut inherited_dimension, total_split_dimension_space, total_inherited_dimension_space) = match layout.children_split_direction { - SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows, total_space_to_split.cols), - SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols, total_space_to_split.rows), + SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows, total_space_to_split.cols, total_space_to_split.rows), + SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols, total_space_to_split.rows, total_space_to_split.cols), }; let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); From 98c95ad6e65c203a12314e34f3eee4591116d528 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 7 Sep 2022 15:16:13 +0200 Subject: [PATCH 19/55] fix(kdl): various bugs --- zellij-utils/assets/config/default.kdl | 18 ++++++++++- zellij-utils/assets/layouts/default.kdl | 8 ++--- zellij-utils/src/input/config.rs | 13 -------- zellij-utils/src/input/layout.rs | 3 +- zellij-utils/src/input/theme.rs | 3 ++ zellij-utils/src/kdl/mod.rs | 31 +++++++++++++++++++ zellij-utils/src/setup.rs | 26 +++++++++++++++- ..._default_config_with_no_cli_arguments.snap | 7 ++++- ...out_env_vars_override_config_env_vars.snap | 7 ++++- ...ayout_plugins_override_config_plugins.snap | 7 ++++- ..._layout_themes_override_config_themes.snap | 7 ++++- ..._ui_config_overrides_config_ui_config.snap | 7 ++++- 12 files changed, 112 insertions(+), 25 deletions(-) diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index dd79ae15b7..374fdb2d9c 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -81,7 +81,7 @@ keybinds { bind "Alt k" "Alt Up" { MoveFocus "Up"; } bind "Alt +" "Alt =" { Resize "Increase"; } bind "Alt -" { Resize "Decrease"; } - bind "c" { SwitchToMode "RenamePane"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0;} } move { bind "Ctrl g" { SwitchToMode "Locked"; } @@ -325,6 +325,22 @@ plugins { // Default: default // theme "dracula" +// 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 +// } +// } + // Choose the mode that zellij uses when starting up. // Default: normal // default_mode "locked" diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index 9103b6e4d1..f8b4595d7b 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -1,11 +1,11 @@ layout { pane size=1 borderless=true { -// plugin location="zellij:tab-bar" + plugin location="zellij:tab-bar" } pane -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } } // keybinds { // normal { diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 55b2260400..97bf493624 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -97,7 +97,6 @@ impl TryFrom<&CliArgs> for Config { if path.exists() { let default_config = Config::from_default_assets()?; Config::from_path(&path, Some(default_config)) - // Config::new(&path) } else { Config::from_default_assets() } @@ -115,11 +114,8 @@ impl Config { } } /// Gets default configuration from assets - // TODO Deserialize the Config from bytes &[u8], - // once serde-yaml supports zero-copy pub fn from_default_assets() -> ConfigResult { let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec())?; - // Self::from_yaml(&cfg) Self::from_kdl(&cfg, None) } pub fn from_path(path: &PathBuf, default_config: Option) -> ConfigResult { @@ -637,15 +633,6 @@ mod config_test { #[test] fn can_define_options_in_configfile() { - // TODO: consider writing a macro to generate a test like this for each option - - - - - - - - let config_contents = r#" simplified_ui true theme "my cool theme" diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 12cbeceac8..3d695f0320 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -180,7 +180,6 @@ impl PaneLayout { } pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { let res = split_space(space, self, space); - log::info!("res: {:#?}", res); res } pub fn extract_run_instructions(&self) -> Vec> { @@ -528,6 +527,7 @@ impl Layout { fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_split: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { let mut pane_positions = Vec::new(); let sizes: Vec> = layout.children.iter().map(|part| part.split_size).collect(); + log::info!("sizes: {:?}", sizes); let mut split_geom = Vec::new(); let (mut current_position, split_dimension_space, mut inherited_dimension, total_split_dimension_space, total_inherited_dimension_space) = @@ -537,6 +537,7 @@ fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_sp }; let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); + log::info!("flex_parts: {:?}", flex_parts); let mut total_pane_size = 0; for (&size, part) in sizes.iter().zip(&*layout.children) { diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index f8f27843ca..d665dae3c4 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -59,6 +59,9 @@ impl Themes { pub fn from_data(theme_data: HashMap) -> Self { Themes(theme_data) } + pub fn insert(&mut self, theme_name: String, theme: Theme) { + self.0.insert(theme_name, theme); + } pub fn merge(&self, mut other: Themes) -> Self { let mut merged = self.clone(); for (name, theme) in other.0.drain() { diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 7c55dc2c1e..eb3810b36f 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -9,6 +9,8 @@ use url::Url; use crate::data::{InputMode, Key, CharOrArrow, PaletteColor, Palette}; use crate::input::options::{Options, OnForceClose, Clipboard}; use std::collections::HashMap; +use std::fs::File; +use std::io::Read; use crate::input::plugins::{PluginsConfig, PluginsConfigError, PluginConfig, PluginType, PluginTag}; use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; use crate::cli::{CliArgs, Command}; @@ -934,3 +936,32 @@ impl Themes { Ok(themes) } } + +impl Theme { + pub fn from_path(path_to_theme_file: PathBuf) -> Result<(String, Self), ConfigError> { + // String is the theme name + let mut file = File::open(path_to_theme_file)?; + let mut kdl_config = String::new(); + file.read_to_string(&mut kdl_config); + let kdl_config: KdlDocument = kdl_config.parse()?; + let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::KdlParsingError("No theme found in file".into()))?; + let theme_name = kdl_name!(kdl_config); + let theme_colors = kdl_children_or_error!(kdl_config, "empty theme"); + Ok((theme_name.into(), Theme { + palette: Palette { + fg: PaletteColor::try_from(("fg", theme_colors))?, + bg: PaletteColor::try_from(("bg", theme_colors))?, + red: PaletteColor::try_from(("red", theme_colors))?, + green: PaletteColor::try_from(("green", theme_colors))?, + yellow: PaletteColor::try_from(("yellow", theme_colors))?, + blue: PaletteColor::try_from(("blue", theme_colors))?, + magenta: PaletteColor::try_from(("magenta", theme_colors))?, + orange: PaletteColor::try_from(("orange", theme_colors))?, + cyan: PaletteColor::try_from(("cyan", theme_colors))?, + black: PaletteColor::try_from(("black", theme_colors))?, + white: PaletteColor::try_from(("white", theme_colors))?, + ..Default::default() + } + })) + } +} diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index ecc45b61a8..f361271bfe 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -10,6 +10,7 @@ use crate::{ options::Options, }, }; +use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; use clap::{Args, IntoApp}; use clap_complete::Shell; use directories_next::BaseDirs; @@ -80,6 +81,9 @@ pub fn get_layout_dir(config_dir: Option) -> Option { config_dir.map(|dir| dir.join("layouts")) } +pub fn get_theme_dir(config_dir: Option) -> Option { + config_dir.map(|dir| dir.join("themes")) +} pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> { std::io::stdout().write_all(asset)?; Ok(()) @@ -210,11 +214,31 @@ impl Setup { } else { None }; - let (layout, config) = Setup::parse_layout_and_override_config(cli_config_options.as_ref(), config, cli_args)?; + let (layout, mut config) = Setup::parse_layout_and_override_config(cli_config_options.as_ref(), config, cli_args)?; let config_options = match cli_config_options { Some(cli_config_options) => config.options.merge(cli_config_options), None => config.options.clone() }; + + if let Some(theme_dir) = config_options + .theme_dir + .clone() + .or_else(|| get_theme_dir(cli_args.config_dir.clone().or_else(find_default_config_dir))) + { + if theme_dir.is_dir() { + for entry in (theme_dir.read_dir()?).flatten() { + if let Some(extension) = entry.path().extension() { + if extension == "kdl" { + if let Ok((theme_name, theme)) = Theme::from_path(entry.path()) { + config.themes.insert(theme_name, theme); + } + } + } + } + } + } + + if let Some(Command::Setup(ref setup)) = &cli_args.command { setup .from_cli_with_options(cli_args, &config_options) diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 52b0b0f6d4..fe9c7dac0d 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 543 +assertion_line: 491 expression: "format!(\"{:#?}\", config)" --- Config { @@ -501,6 +501,11 @@ Config { SwitchToMode( RenamePane, ), + PaneNameInput( + [ + 0, + ], + ), ], Char( 'd', diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 33eeb4ce24..d3e47d11f3 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 593 +assertion_line: 541 expression: "format!(\"{:#?}\", config)" --- Config { @@ -501,6 +501,11 @@ Config { SwitchToMode( RenamePane, ), + PaneNameInput( + [ + 0, + ], + ), ], Char( 'd', diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap index 806c2d8a28..d87904596d 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 609 +assertion_line: 557 expression: "format!(\"{:#?}\", config)" --- Config { @@ -501,6 +501,11 @@ Config { SwitchToMode( RenamePane, ), + PaneNameInput( + [ + 0, + ], + ), ], Char( 'd', 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 a1337407bd..7788377fbb 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: 617 +assertion_line: 565 expression: "format!(\"{:#?}\", config)" --- Config { @@ -501,6 +501,11 @@ Config { SwitchToMode( RenamePane, ), + PaneNameInput( + [ + 0, + ], + ), ], Char( 'd', diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index b9852a90b1..f9a0d4eb12 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 601 +assertion_line: 549 expression: "format!(\"{:#?}\", config)" --- Config { @@ -501,6 +501,11 @@ Config { SwitchToMode( RenamePane, ), + PaneNameInput( + [ + 0, + ], + ), ], Char( 'd', From 29e2abf4aff0a1f4c7ee1b663b13f574649f901d Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 7 Sep 2022 16:03:06 +0200 Subject: [PATCH 20/55] chore(layouts): move all layouts to kdl --- zellij-utils/assets/layouts/compact.kdl | 19 +++---- zellij-utils/assets/layouts/default.kdl | 6 --- .../assets/layouts/disable-status-bar.kdl | 19 +++---- zellij-utils/assets/layouts/no-plugins.kdl | 9 +--- zellij-utils/assets/layouts/strider.kdl | 49 +++++++------------ 5 files changed, 32 insertions(+), 70 deletions(-) diff --git a/zellij-utils/assets/layouts/compact.kdl b/zellij-utils/assets/layouts/compact.kdl index d20ff08af7..7871ad40dd 100644 --- a/zellij-utils/assets/layouts/compact.kdl +++ b/zellij-utils/assets/layouts/compact.kdl @@ -1,13 +1,6 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - body: true - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:compact-bar" +layout { + pane + pane size=1 borderless=true { + plugin location="zellij:compact-bar" + } +} diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index f8b4595d7b..464ae21ecb 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -7,12 +7,6 @@ layout { plugin location="zellij:status-bar" } } -// keybinds { -// normal { -// bind "Ctrl g" { SwitchToMode "Pane"; } -// bind "Ctrl a" { SwitchToMode "Locked"; } -// } -// } // pane_frames false // default_mode "pane" // themes { diff --git a/zellij-utils/assets/layouts/disable-status-bar.kdl b/zellij-utils/assets/layouts/disable-status-bar.kdl index 1077939803..5eca5b0c33 100644 --- a/zellij-utils/assets/layouts/disable-status-bar.kdl +++ b/zellij-utils/assets/layouts/disable-status-bar.kdl @@ -1,13 +1,6 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - - direction: Vertical - body: true +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane +} diff --git a/zellij-utils/assets/layouts/no-plugins.kdl b/zellij-utils/assets/layouts/no-plugins.kdl index 808d686092..4553592469 100644 --- a/zellij-utils/assets/layouts/no-plugins.kdl +++ b/zellij-utils/assets/layouts/no-plugins.kdl @@ -1,8 +1 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - body: true -tabs: - - direction: Vertical +layout diff --git a/zellij-utils/assets/layouts/strider.kdl b/zellij-utils/assets/layouts/strider.kdl index 26e1eba4f7..dbe8d07744 100644 --- a/zellij-utils/assets/layouts/strider.kdl +++ b/zellij-utils/assets/layouts/strider.kdl @@ -1,30 +1,19 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - - direction: Vertical - body: true - - direction: Vertical - borderless: true - split_size: - Fixed: 2 - run: - plugin: - location: "zellij:status-bar" -tabs: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 20 - run: - plugin: - location: "zellij:strider" - - direction: Horizontal +layout { + default_tab_template { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + children + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + tab { + pane split_direction="Vertical" { + pane size="20%" { + plugin location="zellij:strider" + } + pane + } + } +} From c763d195ab4f00d5e044279a724e20dc3005892c Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 7 Sep 2022 18:28:25 +0200 Subject: [PATCH 21/55] feat(kdl): shared keybidns --- zellij-utils/assets/config/default.kdl | 210 ++------ zellij-utils/assets/layouts/compact.yaml | 13 - zellij-utils/assets/layouts/default.kdl | 250 ---------- zellij-utils/assets/layouts/default.yaml | 22 - .../assets/layouts/disable-status-bar.yaml | 13 - zellij-utils/assets/layouts/no-plugins.yaml | 8 - zellij-utils/assets/layouts/strider.yaml | 30 -- zellij-utils/assets/layouts/test-deleteme.kdl | 178 ------- zellij-utils/src/input/config.rs | 57 +++ zellij-utils/src/input/keybinds.rs | 1 - zellij-utils/src/kdl/mod.rs | 56 ++- ..._default_config_with_no_cli_arguments.snap | 466 ++++++++++++++++-- ...out_env_vars_override_config_env_vars.snap | 466 ++++++++++++++++-- ...ayout_plugins_override_config_plugins.snap | 466 ++++++++++++++++-- ..._layout_themes_override_config_themes.snap | 466 ++++++++++++++++-- ..._ui_config_overrides_config_ui_config.snap | 466 ++++++++++++++++-- 16 files changed, 2328 insertions(+), 840 deletions(-) delete mode 100644 zellij-utils/assets/layouts/compact.yaml delete mode 100644 zellij-utils/assets/layouts/default.yaml delete mode 100644 zellij-utils/assets/layouts/disable-status-bar.yaml delete mode 100644 zellij-utils/assets/layouts/no-plugins.yaml delete mode 100644 zellij-utils/assets/layouts/strider.yaml delete mode 100644 zellij-utils/assets/layouts/test-deleteme.kdl diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 374fdb2d9c..0bdab40188 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -1,26 +1,5 @@ keybinds { normal { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl q" { Quit; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt =" "Alt +" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - bind "Alt y" { - Run "diskonaut" { - cwd "/home/aram" - } - } // uncomment this and adjust key if using copy_on_select=false // bind "Alt c" { Copy; } } @@ -28,39 +7,16 @@ keybinds { bind "Ctrl g" { SwitchToMode "Normal"; } } resize { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl q" { Quit; } + bind "Ctrl n" { SwitchToMode "Normal"; } bind "h" "Left" { Resize "Left"; } bind "j" "Down" { Resize "Down"; } bind "k" "Up" { Resize "Up"; } bind "l" "Right" { Resize "Right"; } bind "=" "+" { Resize "Increase"; } bind "-" { Resize "Decrease"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } } pane { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl q" { Quit; } + bind "Ctrl p" { SwitchToMode "Normal"; } bind "h" "Left" { MoveFocus "Left"; } bind "l" "Right" { MoveFocus "Right"; } bind "j" "Down" { MoveFocus "Down"; } @@ -74,48 +30,19 @@ keybinds { bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } bind "w" { ToggleFloatingPanes; SwitchToMode "Normal"; } bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0;} } move { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl q" { Quit; } + bind "Ctrl h" { SwitchToMode "Normal"; } bind "n" "Tab" { MovePane; } bind "h" "Left" { MovePane "Left"; } bind "j" "Down" { MovePane "Down"; } bind "k" "Up" { MovePane "Up"; } bind "l" "Right" { MovePane "Right"; } - bind "Alt n" { NewPane; } - bind "Alt h" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } } tab { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Scroll"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl o" { SwitchToMode "Session"; } + bind "Ctrl t" { SwitchToMode "Normal"; } bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } - bind "Ctrl q" { Quit; } bind "h" "Left" "Up" "k" { GoToPreviousTab; } bind "l" "Right" "Down" "j" { GoToNextTab; } bind "n" { NewTab; SwitchToMode "Normal"; } @@ -131,67 +58,30 @@ keybinds { bind "8" { GoToTab 8; SwitchToMode "Normal"; } bind "9" { GoToTab 9; SwitchToMode "Normal"; } bind "Tab" { ToggleTab; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } } scroll { - bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl s" { SwitchToMode "Normal"; } bind "e" { EditScrollback; SwitchToMode "Normal"; } bind "s" { SwitchToMode "EnterSearch"; } bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } - bind "Ctrl q" { Quit; } bind "j" "Down" { ScrollDown; } bind "k" "Up" { ScrollUp; } bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } bind "d" { HalfPageScrollDown; } bind "u" { HalfPageScrollUp; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } // uncomment this and adjust key if using copy_on_select=false // bind "Alt c" { Copy; } } search { - bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl n" { SwitchToMode "Resize"; } + bind "Ctrl s" { SwitchToMode "Normal"; } bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } bind "j" "Down" { ScrollDown; } bind "k" "Up" { ScrollUp; } - bind "Ctrl q" { Quit; } bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } bind "d" { HalfPageScrollDown; } bind "u" { HalfPageScrollUp; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } bind "n" { Search "down"; } bind "p" { Search "up"; } bind "c" { SearchToggleOption "CaseSensitivity"; } @@ -199,70 +89,25 @@ keybinds { bind "o" { SearchToggleOption "WholeWord"; } } entersearch { - bind "Enter" { SwitchToMode "Search"; } bind "Ctrl c" "Esc" { SwitchToMode "Scroll"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt =" {Resize "Increase"; } - bind "Alt +" {Resize "Increase"; } - bind "Alt -" {Resize "Decrease"; } } renametab { - bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl c" { SwitchToMode "Normal"; } bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt =" {Resize "Increase"; } - bind "Alt +" {Resize "Increase"; } - bind "Alt -" {Resize "Decrease"; } } renamepane { - bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl c" { SwitchToMode "Normal"; } bind "Esc" { UndoRenamePane; SwitchToMode "Pane"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } } session { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + bind "Ctrl o" { SwitchToMode "Normal"; } bind "Ctrl s" { SwitchToMode "Scroll"; } - bind "Ctrl q" { Quit; } bind "d" { Detach; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } } tmux { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } bind "[" { SwitchToMode "Scroll"; } - bind "Ctrl q" { Quit; } bind "Ctrl b" { Write 2; SwitchToMode "Normal"; } - bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "Normal"; } bind "%" { NewPane "Right"; SwitchToMode "Normal"; } bind "z" { ToggleFocusFullscreen; SwitchToMode "Normal"; } bind "c" { NewTab; SwitchToMode "Normal"; } @@ -277,16 +122,43 @@ keybinds { bind "l" { MoveFocus "Right"; SwitchToMode "Normal"; } bind "j" { MoveFocus "Down"; SwitchToMode "Normal"; } bind "k" { MoveFocus "Up"; SwitchToMode "Normal"; } + bind "o" { FocusNextPane; } + bind "d" { Detach; } + } + shared_except "locked" { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl q" { Quit; } bind "Alt n" { NewPane; } bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } bind "Alt j" "Alt Down" { MoveFocus "Down"; } bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "o" { FocusNextPane; } - bind "Alt =" { Resize "Increase"; } - bind "Alt +" { Resize "Increase"; } + bind "Alt =" "Alt +" { Resize "Increase"; } bind "Alt -" { Resize "Decrease"; } - bind "d" { Detach; } + } + shared_except "normal" "locked" { + bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } + } + shared_except "pane" "locked" { + bind "Ctrl p" { SwitchToMode "Pane"; } + } + shared_except "resize" "locked" { + bind "Ctrl n" { SwitchToMode "Resize"; } + } + shared_except "scroll" "locked" { + bind "Ctrl s" { SwitchToMode "Scroll"; } + } + shared_except "session" "locked" { + bind "Ctrl o" { SwitchToMode "Session"; } + } + shared_except "tab" "locked" { + bind "Ctrl t" { SwitchToMode "Tab"; } + } + shared_except "move" "locked" { + bind "Ctrl h" { SwitchToMode "Move"; } + } + shared_except "tmux" "locked" { + bind "Ctrl b" { SwitchToMode "Tmux"; } } } diff --git a/zellij-utils/assets/layouts/compact.yaml b/zellij-utils/assets/layouts/compact.yaml deleted file mode 100644 index d20ff08af7..0000000000 --- a/zellij-utils/assets/layouts/compact.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - body: true - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:compact-bar" diff --git a/zellij-utils/assets/layouts/default.kdl b/zellij-utils/assets/layouts/default.kdl index 464ae21ecb..4434b29b1f 100644 --- a/zellij-utils/assets/layouts/default.kdl +++ b/zellij-utils/assets/layouts/default.kdl @@ -7,253 +7,3 @@ layout { plugin location="zellij:status-bar" } } -// pane_frames false -// default_mode "pane" -// themes { -// dracula { -// // From https://github.com/dracula/zellij -// 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 -// fg 248 248 242 -// cyan 139 233 253 -// black 0 0 0 -// white 255 255 255 -// } -// } -// env { -// RUST_BACKTRACE 2 -// bar "foo" -// } -// layout { -// parts direction="Horizontal" { -// layout size=1 { -// borderless true -// plugin { -// location "zellij:tab-bar" -// } -// } -// tabs; -// layout size=2 { -// borderless true -// plugin { -// location "zellij:status-bar" -// } -// } -// } -// } - -// TODO: CONTINUE HERE (20/08) - consider implementing this -// with tab templates -// layout { -// pane_template name="htop" { -// pane command="htop" -// vertical-sandwich -// foopane -// } -// pane_template name="vertical-sandwich" split_direction="vertical" { -// pane -// children -// pane -// } -// pane_template name="foopane" { -// pane name="foo" -// htop-vertical-sandwich -// } -// pane_template name="htop-vertical-sandwich" split_direction="vertical" { -// pane -// htop -// pane -// } -// htop-vertical-sandwich -// // htop-vertical-sandwich -// // htop -// // pane -// // pane -// } - -// layout { -// tab_template name="horizontal-with-vertical-top" { -// pane split_direction="Vertical" { -// pane -// children -// } -// my-pane-template { -// pane { -// pane -// children -// } -// pane -// } -// pane -// } -// pane_template name="my-pane_template" { -// pane -// children -// } -// horizontal-with-vertical-top name="my tab" { -// pane -// pane -// } -// horizontal-with-vertical-top -// } - - -// layout { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar" -// } -// pane -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } -// -// layout { -// pane_template name="python_pane" { -// pane command="./my-python.script.py" -// } -// default_tab_template { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar" -// } -// children -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } -// tab_template name="my-tab-template" { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar" -// } -// children -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } -// pane_template name="my-cool-pane-tempalte" split_direction="Horiztonal" { -// pane split_direction="Vertical" { -// pane -// children -// pane -// } -// pane -// } -// my-tab-template split_direction="Vertical" { -// my-cool-pane-template { -// pane -// pane -// } -// pane command="htop" -// } -// my-tab-template split_direction="Horizontal" { -// pane -// python_pane -// pane command="bandwhich" -// } -// } - -// layout { -// // tab { -// // pane size=1 borderless=true { -// // plugin location="zellij:tab-bar" -// // } -// // pane -// // pane size=2 borderless=true { -// // plugin location="zellij:status-bar" -// // } -// // } -// tab_template name="not-default" { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar" -// } -// children -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } -// not-default name="first tab" { -// pane -// pane -// } -// not-default name="second tab" split_direction="Vertical" { -// pane -// pane -// } -// not-default -// } - -// layout split_direction="Horizontal" { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar"; -// } -// pane split_direction="Vertical" { -// pane size=1 -// pane -// pane size=2 -// } -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } - -// // without templates -// layout { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar"; -// } -// pane -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } - -// // with pane templates -// layout { -// tab_template name="tab_with_plugins" { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar"; -// } -// children; -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } -// pane_template name="three-vertical-panes" split_direction="Vertical" { -// pane -// pane -// pane -// } -// pane_template name="two_panes_with_child" split_direction="Vertical" { -// pane -// children -// pane -// } -// pane_template name="htop" { -// command "htop" -// cwd "/tmp" -// args "-v" "-h" -// } -// tabs { -// tab_with_plugins name="some tab name" split_direction="Horizontal" focus=true { -// three-vertical-panes -// pane command="diskonaut" cwd="/" -// pane { -// command "bandwhich" -// focus true -// } -// two_panes_with_child { -// htop -// } -// } -// tab_with_plugins -// tab_with_plugins { -// plugin -// htop focused=true -// } -// } -// } diff --git a/zellij-utils/assets/layouts/default.yaml b/zellij-utils/assets/layouts/default.yaml deleted file mode 100644 index 0688e54bbc..0000000000 --- a/zellij-utils/assets/layouts/default.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - - direction: Vertical - body: true - - direction: Vertical - borderless: true - split_size: - Fixed: 2 - run: - plugin: - location: "zellij:status-bar" -tabs: - - direction: Vertical diff --git a/zellij-utils/assets/layouts/disable-status-bar.yaml b/zellij-utils/assets/layouts/disable-status-bar.yaml deleted file mode 100644 index 1077939803..0000000000 --- a/zellij-utils/assets/layouts/disable-status-bar.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - - direction: Vertical - body: true diff --git a/zellij-utils/assets/layouts/no-plugins.yaml b/zellij-utils/assets/layouts/no-plugins.yaml deleted file mode 100644 index 808d686092..0000000000 --- a/zellij-utils/assets/layouts/no-plugins.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - body: true -tabs: - - direction: Vertical diff --git a/zellij-utils/assets/layouts/strider.yaml b/zellij-utils/assets/layouts/strider.yaml deleted file mode 100644 index 26e1eba4f7..0000000000 --- a/zellij-utils/assets/layouts/strider.yaml +++ /dev/null @@ -1,30 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - borderless: true - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - - direction: Vertical - body: true - - direction: Vertical - borderless: true - split_size: - Fixed: 2 - run: - plugin: - location: "zellij:status-bar" -tabs: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 20 - run: - plugin: - location: "zellij:strider" - - direction: Horizontal diff --git a/zellij-utils/assets/layouts/test-deleteme.kdl b/zellij-utils/assets/layouts/test-deleteme.kdl deleted file mode 100644 index 556e2ba686..0000000000 --- a/zellij-utils/assets/layouts/test-deleteme.kdl +++ /dev/null @@ -1,178 +0,0 @@ - -// layout { -// pane_template name="htop" { -// pane command="htop" -// vertical-sandwich { -// pane -// } -// } -// pane_template name="vertical-sandwich" split_direction="vertical" { -// pane -// children -// pane -// } -// pane_template name="htop-vertical-sandwich" split_direction="vertical" { -// pane -// htop -// pane -// } -// htop-vertical-sandwich -// } - -// layout { -// pane_template name="htop" { -// pane command="htop" -// vertical-sandwich -// } -// pane_template name="vertical-sandwich" split_direction="vertical" { -// pane -// children -// } -// pane_template name="htop-vertical-sandwich" split_direction="vertical" { -// pane -// htop -// pane -// } -// htop-vertical-sandwich -// } -// -// layout { -// pane_template name="htop" { -// pane command="htop" -// vertical-sandwich -// } -// pane_template name="vertical-sandwich" split_direction="vertical" { -// pane -// // children -// pane -// pane -// } -// pane_template name="htop-vertical-sandwich" split_direction="vertical" { -// pane -// htop -// pane -// } -// htop-vertical-sandwich -// } -// layout { -// tab_template name="horizontal-with-vertical-top" { -// pane split_direction="Vertical" { -// pane name="1" -// // pane name="3" -// pane { -// pane name="3" -// } -// pane name="4" -// } -// pane name="2" -// } -// // horizontal-with-vertical-top name="my tab" split_direction="Horizontal" { -// // pane -// // pane -// // } -// horizontal-with-vertical-top -// // horizontal-with-vertical-top split_direction="Horizontal" { -// // pane -// // } -// } -// layout { -// pane_template name="htop" { -// pane command="htop" -// vertical-sandwich { -// pane -// } -// } -// pane_template name="vertical-sandwich" split_direction="vertical" { -// pane -// children -// pane -// } -// pane_template name="htop-vertical-sandwich" split_direction="vertical" { -// pane -// htop -// pane -// } -// htop-vertical-sandwich -// } -// layout { -// pane size=1 borderless=true { -// plugin location="zellij:tab-bar" -// } -// pane -// pane size=2 borderless=true { -// plugin location="zellij:status-bar" -// } -// } - -// layout { -// tab_template name="horizontal-with-vertical-top" { -// pane split_direction="Vertical" { -// pane name="3" -// children -// } -// pane name="4" -// } -// horizontal-with-vertical-top name="my tab" { -// pane name="1" -// pane name="2" -// } -// horizontal-with-vertical-top -// } - - -// layout { -// pane_template name="horizontal-with-vertical-top" { -// pane split_direction="Vertical" { -// pane -// children -// } -// pane -// } -// horizontal-with-vertical-top name="my tab" { -// pane -// pane -// } -// horizontal-with-vertical-top -// } -// layout { -// tab_template name="horizontal-with-vertical-top" { -// pane split_direction="Vertical" { -// pane -// children -// } -// pane -// } -// horizontal-with-vertical-top name="my tab" { -// pane -// pane -// } -// horizontal-with-vertical-top -// } -// layout { -// tab_template name="horizontal-with-vertical-top" { -// vertical-sandwich { -// pane name="middle" -// } -// children -// } -// pane_template name="vertical-sandwich" split_direction="Vertical" { -// pane -// children -// pane -// } -// horizontal-with-vertical-top name="my tab" { -// pane -// pane -// } -// horizontal-with-vertical-top -// } - layout { - tab name="my cool tab name 1" - tab name="my cool tab name 2" - default_tab_template { - pane size=1 borderless=true { - plugin location="zellij:tab-bar" - } - pane - } - } diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 97bf493624..24e4f5af6e 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -166,6 +166,7 @@ impl std::error::Error for LayoutNameInTabError { #[cfg(test)] mod config_test { use std::io::Write; + use strum::IntoEnumIterator; use tempfile::tempdir; @@ -610,6 +611,62 @@ mod config_test { assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); } + #[test] + fn can_define_shared_keybinds_for_all_modes() { + let config_contents = r#" + keybinds { + shared { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + for mode in InputMode::iter() { + let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); + } + } + + #[test] + fn can_define_shared_keybinds_with_exclusion() { + let config_contents = r#" + keybinds { + shared_except "locked" { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + for mode in InputMode::iter() { + let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + if mode == InputMode::Locked { + assert_eq!(action_in_mode, None, "Keybind unbound in excluded mode"); + } else { + assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); + } + } + } + + #[test] + fn can_define_shared_keybinds_with_inclusion() { + let config_contents = r#" + keybinds { + shared_among "normal" "resize" "pane" { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + for mode in InputMode::iter() { + let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + if mode == InputMode::Normal || mode == InputMode::Resize || mode == InputMode::Pane { + assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in included mode"); + } else { + assert_eq!(action_in_mode, None, "Keybind unbound in other modes"); + } + } + } + #[test] fn keybindings_unbinds_happen_after_binds() { let config_contents = r#" diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index 715b79c490..cbbc9491fc 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -5,7 +5,6 @@ use std::collections::{BTreeMap, HashMap}; use super::actions::Action; use super::config:: {self, ConfigError}; -use crate::{kdl_arg_is_truthy, kdl_children_or_error, kdl_string_arguments, kdl_argument_values, kdl_children, kdl_name, keys_from_kdl, actions_from_kdl, kdl_children_nodes_or_error}; use crate::data::{InputMode, Key, KeybindsVec}; use serde::{Deserialize, Serialize}; diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index eb3810b36f..f39c14998f 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,5 +1,7 @@ mod kdl_layout_parser; use kdl_layout_parser::KdlLayoutParser; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; use crate::envs::EnvironmentVariables; use crate::input::command::RunCommand; use crate::input::keybinds::Keybinds; @@ -130,7 +132,7 @@ macro_rules! parse_kdl_action_char_or_string_arguments { #[macro_export] macro_rules! kdl_arg_is_truthy { ( $kdl_node:expr, $arg_name:expr ) => { - $kdl_node.get("clear-defaults").and_then(|c| c.value().as_bool()).unwrap_or(false) + $kdl_node.get($arg_name).and_then(|c| c.value().as_bool()).unwrap_or(false) } } @@ -766,23 +768,55 @@ impl EnvironmentVariables { } impl Keybinds { + fn bind_keys_in_block(block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError> { + let bind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); + let unbind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); + for key_block in bind_nodes { + Keybinds::bind_actions_for_each_key(key_block, input_mode_keybinds)?; + } + // we loop twice so that the unbinds always happen after the binds + for key_block in unbind_nodes { + Keybinds::unbind_keys(key_block, input_mode_keybinds)?; + } + Ok(()) + } pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result { let clear_defaults = kdl_arg_is_truthy!(kdl_keybinds, "clear-defaults"); let mut keybinds_from_config = if clear_defaults { Keybinds::default() } else { base_keybinds }; + for block in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { + if kdl_name!(block) == "shared_except" || kdl_name!(block) == "shared" { + let mut modes_to_exclude = vec![]; + for mode_name in kdl_string_arguments!(block) { + modes_to_exclude.push(InputMode::from_str(mode_name)?); + } + for mode in InputMode::iter() { + if modes_to_exclude.contains(&mode) { + continue; + } + let mut input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&mode); + Keybinds::bind_keys_in_block(block, &mut input_mode_keybinds)?; + } + } + if kdl_name!(block) == "shared_among" { + let mut modes_to_include = vec![]; + for mode_name in kdl_string_arguments!(block) { + modes_to_include.push(InputMode::from_str(mode_name)?); + } + for mode in InputMode::iter() { + if !modes_to_include.contains(&mode) { + continue; + } + let mut input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&mode); + Keybinds::bind_keys_in_block(block, &mut input_mode_keybinds)?; + } + } + } for mode in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { - if kdl_name!(mode) == "unbind" { + if kdl_name!(mode) == "unbind" || kdl_name!(mode) == "shared_except" || kdl_name!(mode) == "shared_among" || kdl_name!(mode) == "shared" { continue; } let mut input_mode_keybinds = Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?; - let bind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); - let unbind_nodes = kdl_children_nodes_or_error!(mode, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); - for key_block in bind_nodes { - Keybinds::bind_actions_for_each_key(key_block, &mut input_mode_keybinds)?; - } - // we loop twice so that the unbinds always happen after the binds - for key_block in unbind_nodes { - Keybinds::unbind_keys(key_block, &mut input_mode_keybinds)?; - } + Keybinds::bind_keys_in_block(mode, &mut input_mode_keybinds)?; } if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { Keybinds::unbind_keys_in_all_modes(global_unbind, &mut keybinds_from_config)?; diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index fe9c7dac0d..1162e4825b 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -78,22 +78,6 @@ Config { None, ), ], - Alt( - Char( - 'y', - ), - ): [ - Run( - RunCommandAction { - command: "diskonaut", - args: [], - cwd: Some( - "/home/aram", - ), - direction: None, - }, - ), - ], Alt( Direction( Left, @@ -1412,7 +1396,14 @@ Config { '\n', ): [ SwitchToMode( - Search, + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, ), ], Alt( @@ -1523,6 +1514,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1530,6 +1528,60 @@ Config { Scroll, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ SwitchToMode( Scroll, @@ -1828,6 +1880,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -1936,6 +1995,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1943,6 +2009,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenameTab, SwitchToMode( @@ -1958,6 +2078,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -2066,6 +2193,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -2073,6 +2207,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenamePane, SwitchToMode( @@ -2433,6 +2621,15 @@ Config { None, ), ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], Alt( Direction( Right, @@ -2460,6 +2657,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'g', ): [ @@ -2525,6 +2729,196 @@ Config { ), ], }, + Prompt: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, Tmux: { Left: [ MoveFocus( @@ -2572,6 +2966,18 @@ Config { Normal, ), ], + Char( + '"', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], Char( '%', ): [ @@ -2598,18 +3004,6 @@ Config { Scroll, ), ], - Char( - '\\', - ): [ - NewPane( - Some( - Down, - ), - ), - SwitchToMode( - Normal, - ), - ], Char( 'c', ): [ @@ -2836,6 +3230,13 @@ Config { Resize, ), ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], Ctrl( 'p', ): [ @@ -2848,6 +3249,13 @@ Config { ): [ Quit, ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], Ctrl( 't', ): [ diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index d3e47d11f3..b2f4d6dcae 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -78,22 +78,6 @@ Config { None, ), ], - Alt( - Char( - 'y', - ), - ): [ - Run( - RunCommandAction { - command: "diskonaut", - args: [], - cwd: Some( - "/home/aram", - ), - direction: None, - }, - ), - ], Alt( Direction( Left, @@ -1412,7 +1396,14 @@ Config { '\n', ): [ SwitchToMode( - Search, + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, ), ], Alt( @@ -1523,6 +1514,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1530,6 +1528,60 @@ Config { Scroll, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ SwitchToMode( Scroll, @@ -1828,6 +1880,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -1936,6 +1995,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1943,6 +2009,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenameTab, SwitchToMode( @@ -1958,6 +2078,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -2066,6 +2193,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -2073,6 +2207,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenamePane, SwitchToMode( @@ -2433,6 +2621,15 @@ Config { None, ), ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], Alt( Direction( Right, @@ -2460,6 +2657,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'g', ): [ @@ -2525,6 +2729,196 @@ Config { ), ], }, + Prompt: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, Tmux: { Left: [ MoveFocus( @@ -2572,6 +2966,18 @@ Config { Normal, ), ], + Char( + '"', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], Char( '%', ): [ @@ -2598,18 +3004,6 @@ Config { Scroll, ), ], - Char( - '\\', - ): [ - NewPane( - Some( - Down, - ), - ), - SwitchToMode( - Normal, - ), - ], Char( 'c', ): [ @@ -2836,6 +3230,13 @@ Config { Resize, ), ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], Ctrl( 'p', ): [ @@ -2848,6 +3249,13 @@ Config { ): [ Quit, ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], Ctrl( 't', ): [ diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap index d87904596d..0f1e5a54cb 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap @@ -78,22 +78,6 @@ Config { None, ), ], - Alt( - Char( - 'y', - ), - ): [ - Run( - RunCommandAction { - command: "diskonaut", - args: [], - cwd: Some( - "/home/aram", - ), - direction: None, - }, - ), - ], Alt( Direction( Left, @@ -1412,7 +1396,14 @@ Config { '\n', ): [ SwitchToMode( - Search, + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, ), ], Alt( @@ -1523,6 +1514,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1530,6 +1528,60 @@ Config { Scroll, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ SwitchToMode( Scroll, @@ -1828,6 +1880,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -1936,6 +1995,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1943,6 +2009,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenameTab, SwitchToMode( @@ -1958,6 +2078,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -2066,6 +2193,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -2073,6 +2207,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenamePane, SwitchToMode( @@ -2433,6 +2621,15 @@ Config { None, ), ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], Alt( Direction( Right, @@ -2460,6 +2657,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'g', ): [ @@ -2525,6 +2729,196 @@ Config { ), ], }, + Prompt: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, Tmux: { Left: [ MoveFocus( @@ -2572,6 +2966,18 @@ Config { Normal, ), ], + Char( + '"', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], Char( '%', ): [ @@ -2598,18 +3004,6 @@ Config { Scroll, ), ], - Char( - '\\', - ): [ - NewPane( - Some( - Down, - ), - ), - SwitchToMode( - Normal, - ), - ], Char( 'c', ): [ @@ -2836,6 +3230,13 @@ Config { Resize, ), ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], Ctrl( 'p', ): [ @@ -2848,6 +3249,13 @@ Config { ): [ Quit, ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], Ctrl( 't', ): [ 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 7788377fbb..9e3a8890b2 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 @@ -78,22 +78,6 @@ Config { None, ), ], - Alt( - Char( - 'y', - ), - ): [ - Run( - RunCommandAction { - command: "diskonaut", - args: [], - cwd: Some( - "/home/aram", - ), - direction: None, - }, - ), - ], Alt( Direction( Left, @@ -1412,7 +1396,14 @@ Config { '\n', ): [ SwitchToMode( - Search, + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, ), ], Alt( @@ -1523,6 +1514,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1530,6 +1528,60 @@ Config { Scroll, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ SwitchToMode( Scroll, @@ -1828,6 +1880,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -1936,6 +1995,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1943,6 +2009,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenameTab, SwitchToMode( @@ -1958,6 +2078,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -2066,6 +2193,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -2073,6 +2207,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenamePane, SwitchToMode( @@ -2433,6 +2621,15 @@ Config { None, ), ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], Alt( Direction( Right, @@ -2460,6 +2657,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'g', ): [ @@ -2525,6 +2729,196 @@ Config { ), ], }, + Prompt: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, Tmux: { Left: [ MoveFocus( @@ -2572,6 +2966,18 @@ Config { Normal, ), ], + Char( + '"', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], Char( '%', ): [ @@ -2598,18 +3004,6 @@ Config { Scroll, ), ], - Char( - '\\', - ): [ - NewPane( - Some( - Down, - ), - ), - SwitchToMode( - Normal, - ), - ], Char( 'c', ): [ @@ -2836,6 +3230,13 @@ Config { Resize, ), ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], Ctrl( 'p', ): [ @@ -2848,6 +3249,13 @@ Config { ): [ Quit, ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], Ctrl( 't', ): [ diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index f9a0d4eb12..875d7772d7 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -78,22 +78,6 @@ Config { None, ), ], - Alt( - Char( - 'y', - ), - ): [ - Run( - RunCommandAction { - command: "diskonaut", - args: [], - cwd: Some( - "/home/aram", - ), - direction: None, - }, - ), - ], Alt( Direction( Left, @@ -1412,7 +1396,14 @@ Config { '\n', ): [ SwitchToMode( - Search, + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, ), ], Alt( @@ -1523,6 +1514,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1530,6 +1528,60 @@ Config { Scroll, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ SwitchToMode( Scroll, @@ -1828,6 +1880,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -1936,6 +1995,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -1943,6 +2009,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenameTab, SwitchToMode( @@ -1958,6 +2078,13 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], Alt( Char( '+', @@ -2066,6 +2193,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'c', ): [ @@ -2073,6 +2207,60 @@ Config { Normal, ), ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], Esc: [ UndoRenamePane, SwitchToMode( @@ -2433,6 +2621,15 @@ Config { None, ), ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], Alt( Direction( Right, @@ -2460,6 +2657,13 @@ Config { Down, ), ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], Ctrl( 'g', ): [ @@ -2525,6 +2729,196 @@ Config { ), ], }, + Prompt: { + Char( + '\n', + ): [ + SwitchToMode( + Normal, + ), + ], + Char( + ' ', + ): [ + SwitchToMode( + Normal, + ), + ], + Alt( + Char( + '+', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + '-', + ), + ): [ + Resize( + Decrease, + ), + ], + Alt( + Char( + '=', + ), + ): [ + Resize( + Increase, + ), + ], + Alt( + Char( + 'h', + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Char( + 'j', + ), + ): [ + MoveFocus( + Down, + ), + ], + Alt( + Char( + 'k', + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Char( + 'l', + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Char( + 'n', + ), + ): [ + NewPane( + None, + ), + ], + Alt( + Direction( + Left, + ), + ): [ + MoveFocusOrTab( + Left, + ), + ], + Alt( + Direction( + Right, + ), + ): [ + MoveFocusOrTab( + Right, + ), + ], + Alt( + Direction( + Up, + ), + ): [ + MoveFocus( + Up, + ), + ], + Alt( + Direction( + Down, + ), + ): [ + MoveFocus( + Down, + ), + ], + Ctrl( + 'b', + ): [ + SwitchToMode( + Tmux, + ), + ], + Ctrl( + 'g', + ): [ + SwitchToMode( + Locked, + ), + ], + Ctrl( + 'h', + ): [ + SwitchToMode( + Move, + ), + ], + Ctrl( + 'n', + ): [ + SwitchToMode( + Resize, + ), + ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], + Ctrl( + 'p', + ): [ + SwitchToMode( + Pane, + ), + ], + Ctrl( + 'q', + ): [ + Quit, + ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], + Ctrl( + 't', + ): [ + SwitchToMode( + Tab, + ), + ], + Esc: [ + SwitchToMode( + Normal, + ), + ], + }, Tmux: { Left: [ MoveFocus( @@ -2572,6 +2966,18 @@ Config { Normal, ), ], + Char( + '"', + ): [ + NewPane( + Some( + Down, + ), + ), + SwitchToMode( + Normal, + ), + ], Char( '%', ): [ @@ -2598,18 +3004,6 @@ Config { Scroll, ), ], - Char( - '\\', - ): [ - NewPane( - Some( - Down, - ), - ), - SwitchToMode( - Normal, - ), - ], Char( 'c', ): [ @@ -2836,6 +3230,13 @@ Config { Resize, ), ], + Ctrl( + 'o', + ): [ + SwitchToMode( + Session, + ), + ], Ctrl( 'p', ): [ @@ -2848,6 +3249,13 @@ Config { ): [ Quit, ], + Ctrl( + 's', + ): [ + SwitchToMode( + Scroll, + ), + ], Ctrl( 't', ): [ From dbaa7aeab5218ca29622948a08dff9601561e36c Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Wed, 7 Sep 2022 18:54:02 +0200 Subject: [PATCH 22/55] fix(layout): do not count fixed panes toward percentile --- zellij-utils/src/input/layout.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 3d695f0320..49a229ff71 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -179,8 +179,7 @@ impl PaneLayout { count } pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { - let res = split_space(space, self, space); - res + split_space(space, self, space) } pub fn extract_run_instructions(&self) -> Vec> { let mut run_instructions = vec![]; @@ -545,14 +544,12 @@ fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_sp Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), Some(SplitSize::Fixed(size)) => Dimension::fixed(size), None => { - let total_fixed_size = split_dimension_space.as_usize(); let free_percent = if let Some(p) = split_dimension_space.as_percent() { p - sizes .iter() .map(|&s| { match s { Some(SplitSize::Percent(ip)) => ip as f64, - Some(SplitSize::Fixed(fixed)) => (fixed as f64 / total_fixed_size as f64) * 100.0, _ => 0.0, } }) From 047e75f15adc2a78393beda050810a61a43ff2be Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 8 Sep 2022 09:52:01 +0200 Subject: [PATCH 23/55] fix(keybinds): missing keybinds and actions --- zellij-utils/assets/config/default.kdl | 3 ++- zellij-utils/src/input/layout.rs | 2 -- zellij-utils/src/kdl/mod.rs | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 0bdab40188..658486283a 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -62,7 +62,7 @@ keybinds { scroll { bind "Ctrl s" { SwitchToMode "Normal"; } bind "e" { EditScrollback; SwitchToMode "Normal"; } - bind "s" { SwitchToMode "EnterSearch"; } + bind "s" { SwitchToMode "EnterSearch"; SearchInput 0; } bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } bind "j" "Down" { ScrollDown; } bind "k" "Up" { ScrollUp; } @@ -90,6 +90,7 @@ keybinds { } entersearch { bind "Ctrl c" "Esc" { SwitchToMode "Scroll"; } + bind "Enter" { SwitchToMode "Search"; } } renametab { bind "Ctrl c" { SwitchToMode "Normal"; } diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 49a229ff71..db02aaab00 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -526,7 +526,6 @@ impl Layout { fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_split: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { let mut pane_positions = Vec::new(); let sizes: Vec> = layout.children.iter().map(|part| part.split_size).collect(); - log::info!("sizes: {:?}", sizes); let mut split_geom = Vec::new(); let (mut current_position, split_dimension_space, mut inherited_dimension, total_split_dimension_space, total_inherited_dimension_space) = @@ -536,7 +535,6 @@ fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_sp }; let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); - log::info!("flex_parts: {:?}", flex_parts); let mut total_pane_size = 0; for (&size, part) in sizes.iter().zip(&*layout.children) { diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index f39c14998f..9853af3db6 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -246,6 +246,9 @@ impl Action { "TabNameInput" => { Ok(Action::TabNameInput(bytes)) } + "SearchInput" => { + Ok(Action::SearchInput(bytes)) + } "GoToTab" => { let tab_index = *bytes .get(0) @@ -417,6 +420,7 @@ impl TryFrom<&KdlNode> for Action { "NewTab" => Ok(Action::NewTab(None, None)), // TODO: consider the Some(TabLayout, "tab_name") case... "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "SearchInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "SearchToggleOption" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "Run" => { let arguments = action_arguments.iter().copied(); From 789dec36c3fe78c5a039699c1fee69f078d037b1 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 8 Sep 2022 10:19:57 +0200 Subject: [PATCH 24/55] fix(config): adjust default tips --- zellij-utils/assets/config/default.kdl | 112 +-- zellij-utils/assets/config/default.yaml | 639 ------------------ .../assets/config/test_config_deleteme.kdl | 378 ----------- ...efault_config_with_no_cli_arguments-2.snap | 2 +- ..._default_config_with_no_cli_arguments.snap | 7 +- ...out_env_vars_override_config_env_vars.snap | 7 +- ...ayout_plugins_override_config_plugins.snap | 7 +- ..._layout_themes_override_config_themes.snap | 7 +- ..._ui_config_overrides_config_ui_config.snap | 7 +- 9 files changed, 95 insertions(+), 1071 deletions(-) delete mode 100644 zellij-utils/assets/config/default.yaml delete mode 100644 zellij-utils/assets/config/test_config_deleteme.kdl diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 658486283a..02141925d6 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -170,34 +170,37 @@ plugins { compact-bar { path "compact-bar"; } } -// 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 +// 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" // Send a request for a simplified ui (without arrow fonts) to plugins // Options: // - true // - false (Default) +// // simplified_ui true -// Choose the path to the default shell that zellij will use for opening new panes -// Default: $SHELL +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// // default_shell "fish" -// Toggle between having pane frames around the panes -// Options: -// - true (default) -// - false +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// // pane_frames true -// Choose the theme that is specified in the themes section. -// For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes -// Default: default -// theme "dracula" - +// Define color themes for Zellij +// For more examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +// Once these themes are defined, one of them should to be selected in the "theme" section of this file +// // themes { // dracula { // fg 248 248 242 @@ -214,52 +217,65 @@ plugins { // } // } -// Choose the mode that zellij uses when starting up. -// Default: normal +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// // default_mode "locked" -// Toggle enabling the mouse mode. -// On certain configurations, or terminals this could -// potentially interfere with copying text. -// Options: -// - true (default) -// - false +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// // mouse_mode false -// 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 +// 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: +// 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 -// 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 +// 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 or disable automatic copy (and clear) of selection when releasing mouse -// Default: true +// Enable or disable automatic copy (and clear) 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 +// Path to the default editor to use to edit pane scrollbuffer +// Default: $EDITOR or $VISUAL +// // scrollback_editor "/usr/bin/vim" -// 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 +// 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 diff --git a/zellij-utils/assets/config/default.yaml b/zellij-utils/assets/config/default.yaml deleted file mode 100644 index 2aecc6d836..0000000000 --- a/zellij-utils/assets/config/default.yaml +++ /dev/null @@ -1,639 +0,0 @@ ---- -# Configuration for zellij. - -# In order to troubleshoot your configuration try using the following command: -# `zellij setup --check` -# It should show current config locations and features that are enabled. - -keybinds: - unbind: true - normal: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g',] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's',] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [NewPane: ] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right ] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up, ] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - # uncomment this and adjust key if using copy_on_select=false - # - action: [Copy: ] - # key: [ Alt: 'c'] - locked: - - action: [SwitchToMode: Normal,] - key: [Ctrl: 'g',] - resize: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g'] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's'] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [Quit] - key: [Ctrl: 'q'] - - action: [Resize: Left,] - key: [Char: 'h', Left,] - - action: [Resize: Down,] - key: [Char: 'j', Down,] - - action: [Resize: Up,] - key: [Char: 'k', Up, ] - - action: [Resize: Right,] - key: [Char: 'l', Right,] - - action: [Resize: Increase,] - key: [Char: '='] - - action: [Resize: Increase,] - key: [ Char: '+'] - - action: [Resize: Decrease,] - key: [Char: '-'] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - pane: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g'] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's'] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [MoveFocus: Left,] - key: [ Char: 'h', Left,] - - action: [MoveFocus: Right,] - key: [ Char: 'l', Right,] - - action: [MoveFocus: Down,] - key: [ Char: 'j', Down,] - - action: [MoveFocus: Up,] - key: [ Char: 'k', Up,] - - action: [SwitchFocus,] - key: [Char: 'p'] - - action: [NewPane: , SwitchToMode: Normal,] - key: [Char: 'n',] - - action: [NewPane: Down, SwitchToMode: Normal,] - key: [Char: 'd',] - - action: [NewPane: Right, SwitchToMode: Normal,] - key: [Char: 'r',] - - action: [CloseFocus, SwitchToMode: Normal,] - key: [Char: 'x',] - - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] - key: [Char: 'f',] - - action: [TogglePaneFrames, SwitchToMode: Normal,] - key: [Char: 'z',] - - action: [ToggleFloatingPanes, SwitchToMode: Normal,] - key: [Char: 'w'] - - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] - key: [Char: 'e'] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] - key: [Char: 'c'] - move: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g'] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's'] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [Quit] - key: [Ctrl: 'q'] - - action: [MovePane: ,] - key: [Char: 'n', Char: "\t",] - - action: [MovePane: Left,] - key: [Char: 'h', Left,] - - action: [MovePane: Down,] - key: [Char: 'j', Down,] - - action: [MovePane: Up,] - key: [Char: 'k', Up, ] - - action: [MovePane: Right,] - key: [Char: 'l', Right,] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - tab: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g'] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's'] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [SwitchToMode: RenameTab, TabNameInput: [0],] - key: [Char: 'r'] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [GoToPreviousTab,] - key: [ Char: 'h', Left, Up, Char: 'k',] - - action: [GoToNextTab,] - key: [ Char: 'l', Right,Down, Char: 'j'] - - action: [NewTab: , SwitchToMode: Normal,] - key: [ Char: 'n',] - - action: [CloseTab, SwitchToMode: Normal,] - key: [ Char: 'x',] - - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] - key: [Char: 's'] - - action: [GoToTab: 1, SwitchToMode: Normal,] - key: [ Char: '1',] - - action: [GoToTab: 2, SwitchToMode: Normal,] - key: [ Char: '2',] - - action: [GoToTab: 3, SwitchToMode: Normal,] - key: [ Char: '3',] - - action: [GoToTab: 4, SwitchToMode: Normal,] - key: [ Char: '4',] - - action: [GoToTab: 5, SwitchToMode: Normal,] - key: [ Char: '5',] - - action: [GoToTab: 6, SwitchToMode: Normal,] - key: [ Char: '6',] - - action: [GoToTab: 7, SwitchToMode: Normal,] - key: [ Char: '7',] - - action: [GoToTab: 8, SwitchToMode: Normal,] - key: [ Char: '8',] - - action: [GoToTab: 9, SwitchToMode: Normal,] - key: [ Char: '9',] - - action: [ToggleTab] - key: [ Char: "\t" ] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - scroll: - - action: [EditScrollback, SwitchToMode: Normal] - key: [Char: 'e'] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g',] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [ScrollToBottom, SwitchToMode: Normal,] - key: [Ctrl: 'c',] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [ScrollDown,] - key: [Char: 'j', Down,] - - action: [ScrollUp,] - key: [Char: 'k', Up,] - - action: [PageScrollDown,] - key: [Ctrl: 'f', PageDown, Right, Char: 'l',] - - action: [PageScrollUp,] - key: [Ctrl: 'b', PageUp, Left, Char: 'h',] - - action: [HalfPageScrollDown,] - key: [Char: 'd',] - - action: [HalfPageScrollUp,] - key: [Char: 'u',] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - # uncomment this and adjust key if using copy_on_select=false - # - action: [Copy: ] - # key: [ Alt: 'c'] - - action: [SwitchToMode: EnterSearch, SearchInput: [0],] - key: [Char: 's'] - entersearch: - - action: [SwitchToMode: Search,] - key: [Char: "\n"] - - action: [SearchInput: [27], SwitchToMode: Scroll,] - key: [Ctrl: 'c', Esc] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - search: - - action: [SwitchToMode: Normal,] - key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g',] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [SwitchToMode: Session,] - key: [Ctrl: 'o',] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [ScrollToBottom, SwitchToMode: Normal,] - key: [Ctrl: 'c',] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [ScrollDown,] - key: [Char: 'j', Down,] - - action: [ScrollUp,] - key: [Char: 'k', Up,] - - action: [PageScrollDown,] - key: [Ctrl: 'f', PageDown, Right, Char: 'l',] - - action: [PageScrollUp,] - key: [Ctrl: 'b', PageUp, Left, Char: 'h',] - - action: [HalfPageScrollDown,] - key: [Char: 'd',] - - action: [HalfPageScrollUp,] - key: [Char: 'u',] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - - action: [SwitchToMode: EnterSearch, SearchInput: [0],] - key: [Char: 's'] - - action: [Search: Down] - key: [Char: 'n'] - - action: [Search: Up] - key: [Char: 'p'] - - action: [SearchToggleOption: CaseSensitivity] - key: [Char: 'c'] - - action: [SearchToggleOption: Wrap] - key: [Char: 'w'] - - action: [SearchToggleOption: WholeWord] - key: [Char: 'o'] - renametab: - - action: [SwitchToMode: Normal,] - key: [Char: "\n", Ctrl: 'c', Esc] - - action: [UndoRenameTab , SwitchToMode: Tab,] - key: [Esc,] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - renamepane: - - action: [SwitchToMode: Normal,] - key: [Char: "\n", Ctrl: 'c', Esc] - - action: [UndoRenamePane , SwitchToMode: Pane,] - key: [Esc,] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - session: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g'] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tmux,] - key: [Ctrl: 'b',] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's'] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [Detach,] - key: [Char: 'd',] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - tmux: - - action: [SwitchToMode: Locked,] - key: [Ctrl: 'g'] - - action: [SwitchToMode: Resize,] - key: [Ctrl: 'n',] - - action: [SwitchToMode: Pane,] - key: [Ctrl: 'p',] - - action: [SwitchToMode: Move,] - key: [Ctrl: 'h',] - - action: [SwitchToMode: Tab,] - key: [Ctrl: 't',] - - action: [SwitchToMode: Normal,] - key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] - - action: [SwitchToMode: Scroll,] - key: [Ctrl: 's'] - - action: [SwitchToMode: Scroll,] - key: [ Char: '['] - - action: [Quit,] - key: [Ctrl: 'q',] - - action: [Write: [2,], SwitchToMode: Normal] - key: [Ctrl: 'b'] - - action: [NewPane: Down, SwitchToMode: Normal,] - key: [Char: "\"",] - - action: [NewPane: Right, SwitchToMode: Normal,] - key: [Char: '%',] - - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] - key: [Char: 'z',] - - action: [NewTab: , SwitchToMode: Normal,] - key: [ Char: 'c',] - - action: [SwitchToMode: RenameTab, TabNameInput: [0],] - key: [Char: ','] - - action: [GoToPreviousTab, SwitchToMode: Normal,] - key: [ Char: 'p'] - - action: [GoToNextTab, SwitchToMode: Normal,] - key: [ Char: 'n'] - - action: [MoveFocus: Left, SwitchToMode: Normal,] - key: [ Left,] - - action: [MoveFocus: Right, SwitchToMode: Normal,] - key: [ Right,] - - action: [MoveFocus: Down, SwitchToMode: Normal,] - key: [ Down,] - - action: [MoveFocus: Up, SwitchToMode: Normal,] - key: [ Up,] - - action: [MoveFocus: Left, SwitchToMode: Normal,] - key: [ Char: 'h'] - - action: [MoveFocus: Right, SwitchToMode: Normal,] - key: [ Char: 'l'] - - action: [MoveFocus: Down, SwitchToMode: Normal,] - key: [ Char: 'j'] - - action: [MoveFocus: Up, SwitchToMode: Normal,] - key: [ Char: 'k'] - - action: [NewPane: ,] - key: [ Alt: 'n',] - - action: [MoveFocusOrTab: Left,] - key: [ Alt: 'h', Alt: Left] - - action: [MoveFocusOrTab: Right,] - key: [ Alt: 'l', Alt: Right] - - action: [MoveFocus: Down,] - key: [ Alt: 'j', Alt: Down] - - action: [MoveFocus: Up,] - key: [ Alt: 'k', Alt: Up] - - action: [FocusNextPane,] - key: [ Char: 'o'] - - action: [Resize: Increase,] - key: [ Alt: '='] - - action: [Resize: Increase,] - key: [ Alt: '+'] - - action: [Resize: Decrease,] - key: [ Alt: '-'] - - action: [Detach,] - key: [Char: 'd',] -plugins: - - path: tab-bar - tag: tab-bar - - path: status-bar - tag: status-bar - - path: strider - tag: strider - - path: compact-bar - tag: compact-bar - -# 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 - -# Send a request for a simplified ui (without arrow fonts) to plugins -# Options: -# - true -# - false (Default) -#simplified_ui: true - -# Choose the path to the default shell that zellij will use for opening new panes -# Default: $SHELL -# default_shell: fish - -# Toggle between having pane frames around the panes -# Options: -# - true (default) -# - false -#pane_frames: true - -# Choose the theme that is specified in the themes section. -# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes -# Default: default -#theme: default - -# Choose the mode that zellij uses when starting up. -# Default: normal -#default_mode: locked - -# Toggle enabling the mouse mode. -# On certain configurations, or terminals this could -# potentially interfere with copying text. -# Options: -# - true (default) -# - false -#mouse_mode: false - -# 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 - -# 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 or disable automatic copy (and clear) of selection when releasing mouse -#copy_on_select: true - -# Path to the default editor to use to edit pane scrollbuffer -# scrollback_editor: /usr/bin/nano diff --git a/zellij-utils/assets/config/test_config_deleteme.kdl b/zellij-utils/assets/config/test_config_deleteme.kdl deleted file mode 100644 index 658cd1d41e..0000000000 --- a/zellij-utils/assets/config/test_config_deleteme.kdl +++ /dev/null @@ -1,378 +0,0 @@ -keybinds { - unbind "Ctrl g"; - normal clear-defaults=true { - unbind "Alt ="; // TODO: removeme - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl s" { SwitchToMode "Search"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl q" { Quit; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt =" "Alt +" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - bind "Alt y" { - Run "diskonaut" { - cwd "/home/aram" - } - } - // uncomment this and adjust key if using copy_on_select=false - // bind "Alt c" { Copy; } - } - locked { - bind "Ctrl g" { SwitchToMode "Normal"; } - } - resize { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl q" { Quit; } - bind "h" "Left" { Resize "Left"; } - bind "j" "Down" { Resize "Down"; } - bind "k" "Up" { Resize "Up"; } - bind "l" "Right" { Resize "Right"; } - bind "=" "+" { Resize "Increase"; } - bind "-" { Resize "Decrease"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - } - pane { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl q" { Quit; } - bind "h" "Left" { MoveFocus "Left"; } - bind "l" "Right" { MoveFocus "Right"; } - bind "j" "Down" { MoveFocus "Down"; } - bind "k" "Up" { MoveFocus "Up"; } - bind "p" { SwitchFocus; } - bind "n" { NewPane; SwitchToMode "Normal"; } - bind "d" { NewPane "Down"; SwitchToMode "Normal"; } - bind "r" { NewPane "Right"; SwitchToMode "Normal"; } - bind "x" { CloseFocus; SwitchToMode "Normal"; } - bind "f" { ToggleFocusFullscreen; SwitchToMode "Normal"; } - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - bind "w" { ToggleFloatingPanes; SwitchToMode "Normal"; } - bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - bind "c" { SwitchToMode "RenamePane"; } - } - move { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl q" { Quit; } - bind "n" "Tab" { MovePane; } - bind "h" "Left" { MovePane "Left"; } - bind "j" "Down" { MovePane "Down"; } - bind "k" "Up" { MovePane "Up"; } - bind "l" "Right" { MovePane "Right"; } - bind "Alt n" { NewPane; } - bind "Alt h" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - } - tab { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } - bind "Ctrl q" { Quit; } - bind "h" "Left" "Up" "k" { GoToPreviousTab; } - bind "l" "Right" "Down" "j" { GoToNextTab; } - bind "n" { NewTab; SwitchToMode "Normal"; } - bind "x" { CloseTab; SwitchToMode "Normal"; } - bind "s" { ToggleActiveSyncTab; SwitchToMode "Normal"; } - 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 "Tab" { ToggleTab; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - } - search { - bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl o" { SwitchToMode "Session"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "e" { EditScrollback; SwitchToMode "Normal"; } - bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; } - bind "Ctrl q" { Quit; } - bind "j" "Down" { ScrollDown; } - bind "k" "Up" { ScrollUp; } - bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } - bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } - bind "d" { HalfPageScrollDown; } - bind "u" { HalfPageScrollUp; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - // uncomment this and adjust key if using copy_on_select=false - // bind "Alt c" { Copy; } - } - renametab { - bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } - bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt =" {Resize "Increase"; } - bind "Alt +" {Resize "Increase"; } - bind "Alt -" {Resize "Decrease"; } - } - renamepane { - bind "Enter" "Ctrl c" "Esc" { SwitchToMode "Normal"; } - bind "Esc" { UndoRenamePane; SwitchToMode "Pane"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - } - session { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl b" { SwitchToMode "Tmux"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "Ctrl s" { SwitchToMode "Search"; } - bind "Ctrl q" { Quit; } - bind "d" { Detach; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "Alt +" "Alt =" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - } - tmux { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "Ctrl n" { SwitchToMode "Resize"; } - bind "Ctrl p" { SwitchToMode "Pane"; } - bind "Ctrl h" { SwitchToMode "Move"; } - bind "Ctrl t" { SwitchToMode "Tab"; } - bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } - bind "[" { SwitchToMode "Search"; } - bind "Ctrl q" { Quit; } - bind "Ctrl b" { Write 2; SwitchToMode "Normal"; } - bind "\\" { NewPane "Down"; SwitchToMode "Normal"; } - bind "%" { NewPane "Right"; SwitchToMode "Normal"; } - bind "z" { ToggleFocusFullscreen; SwitchToMode "Normal"; } - bind "c" { NewTab; SwitchToMode "Normal"; } - bind "," { SwitchToMode "RenameTab"; } - bind "p" { GoToPreviousTab; SwitchToMode "Normal"; } - bind "n" { GoToNextTab; SwitchToMode "Normal"; } - bind "Left" { MoveFocus "Left"; SwitchToMode "Normal"; } - bind "Right" { MoveFocus "Right"; SwitchToMode "Normal"; } - bind "Down" { MoveFocus "Down"; SwitchToMode "Normal"; } - bind "Up" { MoveFocus "Up"; SwitchToMode "Normal"; } - bind "h" { MoveFocus "Left"; SwitchToMode "Normal"; } - bind "l" { MoveFocus "Right"; SwitchToMode "Normal"; } - bind "j" { MoveFocus "Down"; SwitchToMode "Normal"; } - bind "k" { MoveFocus "Up"; SwitchToMode "Normal"; } - bind "Alt n" { NewPane; } - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } - bind "Alt j" "Alt Down" { MoveFocus "Down"; } - bind "Alt k" "Alt Up" { MoveFocus "Up"; } - bind "o" { FocusNextPane; } - bind "Alt =" { Resize "Increase"; } - bind "Alt +" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - bind "d" { Detach; } - } -} -// session_name "my-foo-session" -// attach_to_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" - -// Send a request for a simplified ui (without arrow fonts) to plugins -// Options: -// - true -// - false (Default) -// simplified_ui true - -// Choose the path to the default shell that zellij will use for opening new panes -// Default: $SHELL -// default_shell "fish" - -// Toggle between having pane frames around the panes -// Options: -// - true (default) -// - false -// pane_frames true - -// Choose the theme that is specified in the themes section. -// For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes -// Default: default -// theme "default" - -// Choose the mode that zellij uses when starting up. -// Default: normal -// default_mode "locked" - -// Toggle enabling the mouse mode. -// On certain configurations, or terminals this could -// potentially interfere with copying text. -// Options: -// - true (default) -// - false -// mouse_mode false - -// 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 - -// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" - -// 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 - -themes { - dracula { - // From https://github.com/dracula/zellij - 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 - fg 248 248 242 - cyan 139 233 253 - black 0 0 0 - white 255 255 255 - } - nord { - fg "#D8DEE9" - bg "#2E3440" - black "#3B4252" - red "#BF616A" - green "#A3BE8C" - yellow "#EBCB8B" - blue "#81A1C1" - magenta "#B48EAD" - cyan "#88C0D0" - white "#E5E9F0" - orange "#D08770" - } -} - -plugins { - tab-bar { path "tab-bar"; } - status-bar { path "status-bar"; } - strider { path "strider"; } - compact-bar { path "compact-bar"; } -} - -ui { - pane_frames { - rounded_corners false - } -} - -env { - RUST_BACKTRACE 1 - foo "bar" -} diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap index 4611ca2d53..d4ea72ea4c 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 468 +assertion_line: 492 expression: "format!(\"{:#?}\", layout)" --- Layout { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 1162e4825b..35cd5f7e04 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -1199,6 +1199,11 @@ Config { SwitchToMode( EnterSearch, ), + SearchInput( + [ + 0, + ], + ), ], Char( 'u', @@ -1396,7 +1401,7 @@ Config { '\n', ): [ SwitchToMode( - Normal, + Search, ), ], Char( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index b2f4d6dcae..07c91c5aa4 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -1199,6 +1199,11 @@ Config { SwitchToMode( EnterSearch, ), + SearchInput( + [ + 0, + ], + ), ], Char( 'u', @@ -1396,7 +1401,7 @@ Config { '\n', ): [ SwitchToMode( - Normal, + Search, ), ], Char( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap index 0f1e5a54cb..5a71f06ae6 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap @@ -1199,6 +1199,11 @@ Config { SwitchToMode( EnterSearch, ), + SearchInput( + [ + 0, + ], + ), ], Char( 'u', @@ -1396,7 +1401,7 @@ Config { '\n', ): [ SwitchToMode( - Normal, + Search, ), ], Char( 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 9e3a8890b2..1af931cec2 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 @@ -1199,6 +1199,11 @@ Config { SwitchToMode( EnterSearch, ), + SearchInput( + [ + 0, + ], + ), ], Char( 'u', @@ -1396,7 +1401,7 @@ Config { '\n', ): [ SwitchToMode( - Normal, + Search, ), ], Char( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index 875d7772d7..1e19ab48c5 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -1199,6 +1199,11 @@ Config { SwitchToMode( EnterSearch, ), + SearchInput( + [ + 0, + ], + ), ], Char( 'u', @@ -1396,7 +1401,7 @@ Config { '\n', ): [ SwitchToMode( - Normal, + Search, ), ], Char( From 847b9ec85690befacdc70030bb31bc4639398bc2 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 8 Sep 2022 11:34:58 +0200 Subject: [PATCH 25/55] refactor(config): move stuff around --- zellij-server/src/lib.rs | 2 - zellij-server/src/pty.rs | 3 - zellij-utils/src/input/actions.rs | 10 - zellij-utils/src/input/config.rs | 459 ------ zellij-utils/src/input/keybinds.rs | 146 -- zellij-utils/src/input/plugins.rs | 164 --- zellij-utils/src/input/theme.rs | 105 +- .../input/unit/fixtures/themes/dracula.kdl | 16 + .../input/unit/fixtures/themes/dracula.yaml | 16 - .../src/input/unit/fixtures/themes/nord.kdl | 15 + .../src/input/unit/fixtures/themes/nord.yaml | 16 - zellij-utils/src/input/unit/keybinds_test.rs | 1267 ++++++----------- zellij-utils/src/input/unit/layout_test.rs | 879 ------------ ..._layout_test__dracula_theme_from_file.snap | 109 ++ zellij-utils/src/input/unit/theme_test.rs | 15 +- zellij-utils/src/ipc.rs | 1 - 16 files changed, 612 insertions(+), 2611 deletions(-) create mode 100644 zellij-utils/src/input/unit/fixtures/themes/dracula.kdl delete mode 100644 zellij-utils/src/input/unit/fixtures/themes/dracula.yaml create mode 100644 zellij-utils/src/input/unit/fixtures/themes/nord.kdl delete mode 100644 zellij-utils/src/input/unit/fixtures/themes/nord.yaml create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__layout_test__dracula_theme_from_file.snap diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index bb507ed062..0d4f8589d0 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -61,7 +61,6 @@ pub enum ServerInstruction { ClientAttributes, Box, Box, - // Box, Box, ClientId, Option, @@ -612,7 +611,6 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { pub struct SessionOptions { pub opts: Box, pub config_options: Box, - // pub layout: Box, pub layout: Box, pub plugins: Option, } diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 3617564f56..d3e9ccc54a 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -66,9 +66,6 @@ pub(crate) struct Pty { default_editor: Option, } -use std::convert::TryFrom; - -// pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { loop { let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel"); diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 6baa77c6c2..5cca66f9a6 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -206,13 +206,3 @@ impl From for Action { } } } - -// #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -// pub struct ActionsFromYaml(Vec); - -// impl ActionsFromYaml { -// /// Get a reference to the actions from yaml's actions. -// pub fn actions(&self) -> &[Action] { -// self.0.as_ref() -// } -// } diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 24e4f5af6e..f3a8520532 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -229,465 +229,6 @@ mod config_test { assert_eq!(result.unwrap(), Config::from_default_assets().unwrap()); } - #[test] - fn can_define_keybindings_in_configfile() { - let config_contents = r#" - keybinds { - normal { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - let ctrl_g_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); - assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding successfully defined in config"); - } - - #[test] - fn can_define_multiple_keybinds_for_same_action() { - let config_contents = r#" - keybinds { - normal { - bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - let alt_h_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Alt(CharOrArrow::Direction(data::Direction::Left)) - ); - let alt_left_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Alt(CharOrArrow::Char('h')) - ); - assert_eq!(alt_h_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "First keybinding successfully defined in config"); - assert_eq!(alt_left_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "Second keybinding successfully defined in config"); - } - - #[test] - fn can_define_series_of_actions_for_same_keybinding() { - let config_contents = r#" - keybinds { - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Action series successfully defined"); - } - - #[test] - fn keybindings_bind_order_is_preserved() { - let config_contents = r#" - keybinds { - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - bind "z" { SwitchToMode "Resize"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Second keybinding was applied"); - } - - #[test] - fn uppercase_and_lowercase_keybindings_are_distinct() { - let config_contents = r#" - keybinds { - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - bind "Z" { SwitchToMode "Resize"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - let uppercase_z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('Z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Lowercase z successfully bound"); - assert_eq!(uppercase_z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Uppercase z successfully bound"); - } - - #[test] - fn can_override_keybindings() { - let default_config_contents = r#" - keybinds { - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - } - } - "#; - let config_contents = r#" - keybinds { - pane { - bind "z" { SwitchToMode "Resize"; } - } - } - "#; - let default_config = Config::from_kdl(default_config_contents, None).unwrap(); - let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from config overrode keybinding from default config"); - } - - #[test] - fn can_add_to_default_keybindings() { - // this test just makes sure keybindings defined in a custom config are added to different - // keybindings defined in the default config - let default_config_contents = r#" - keybinds { - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - } - } - "#; - let config_contents = r#" - keybinds { - pane { - bind "r" { SwitchToMode "Resize"; } - } - } - "#; - let default_config = Config::from_kdl(default_config_contents, None).unwrap(); - let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - let r_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Keybinding from default config bound"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from custom config bound as well"); - } - - #[test] - fn can_clear_default_keybindings() { - let default_config_contents = r#" - keybinds { - normal { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - } - } - "#; - let config_contents = r#" - keybinds clear-defaults=true { - normal { - bind "Ctrl r" { SwitchToMode "Locked"; } - } - pane { - bind "r" { SwitchToMode "Resize"; } - } - } - "#; - let default_config = Config::from_kdl(default_config_contents, None).unwrap(); - let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - let ctrl_r_in_normal_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Ctrl('r'), - ); - let r_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - assert_eq!(ctrl_g_normal_mode_action, None, "Keybinding from normal mode in default config cleared"); - assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); - assert_eq!(ctrl_r_in_normal_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding from normal mode in custom config still bound"); - } - - #[test] - fn can_clear_default_keybindings_per_single_mode() { - let default_config_contents = r#" - keybinds { - normal { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - pane { - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - } - } - "#; - let config_contents = r#" - keybinds { - pane clear-defaults=true { - bind "r" { SwitchToMode "Resize"; } - } - } - "#; - let default_config = Config::from_kdl(default_config_contents, None).unwrap(); - let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Ctrl('g'), - ); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - let r_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode from default config not cleared"); - assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); - } - - #[test] - fn can_unbind_multiple_keys_globally() { - let default_config_contents = r#" - keybinds { - normal { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - pane { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - bind "r" { TogglePaneFrames; } - } - } - "#; - let config_contents = r#" - keybinds { - unbind "Ctrl g" "z" - pane { - bind "t" { SwitchToMode "Tab"; } - } - } - "#; - let default_config = Config::from_kdl(default_config_contents, None).unwrap(); - let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Ctrl('g'), - ); - let ctrl_g_pane_mode_action = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Ctrl('g'), - ); - let r_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - let t_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('t'), - ); - assert_eq!(ctrl_g_normal_mode_action, None, "First keybind uncleared in one mode"); - assert_eq!(ctrl_g_pane_mode_action, None, "First keybind uncleared in another mode"); - assert_eq!(z_in_pane_mode, None, "Second keybind cleared as well"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); - assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); - } - - #[test] - fn can_unbind_multiple_keys_per_single_mode() { - let default_config_contents = r#" - keybinds { - normal { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - pane { - bind "Ctrl g" { SwitchToMode "Locked"; } - bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } - bind "r" { TogglePaneFrames; } - } - } - "#; - let config_contents = r#" - keybinds { - pane { - unbind "Ctrl g" "z" - bind "t" { SwitchToMode "Tab"; } - } - } - "#; - let default_config = Config::from_kdl(default_config_contents, None).unwrap(); - let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); - let ctrl_g_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); - let ctrl_g_pane_mode_action = config - .keybinds - .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g')); - let r_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - let t_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('t'), - ); - assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode not cleared"); - assert_eq!(ctrl_g_pane_mode_action, None, "First Keybind cleared in its mode"); - assert_eq!(z_in_pane_mode, None, "Second keybind cleared in its mode as well"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); - assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); - } - - #[test] - fn can_define_shared_keybinds_for_all_modes() { - let config_contents = r#" - keybinds { - shared { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - for mode in InputMode::iter() { - let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); - assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); - } - } - - #[test] - fn can_define_shared_keybinds_with_exclusion() { - let config_contents = r#" - keybinds { - shared_except "locked" { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - for mode in InputMode::iter() { - let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); - if mode == InputMode::Locked { - assert_eq!(action_in_mode, None, "Keybind unbound in excluded mode"); - } else { - assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); - } - } - } - - #[test] - fn can_define_shared_keybinds_with_inclusion() { - let config_contents = r#" - keybinds { - shared_among "normal" "resize" "pane" { - bind "Ctrl g" { SwitchToMode "Locked"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - for mode in InputMode::iter() { - let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); - if mode == InputMode::Normal || mode == InputMode::Resize || mode == InputMode::Pane { - assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in included mode"); - } else { - assert_eq!(action_in_mode, None, "Keybind unbound in other modes"); - } - } - } - - #[test] - fn keybindings_unbinds_happen_after_binds() { - let config_contents = r#" - keybinds { - pane { - unbind "z" - bind "z" { SwitchToMode "Resize"; } - } - } - "#; - let config = Config::from_kdl(config_contents, None).unwrap(); - let z_in_pane_mode = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound"); - } - - #[test] fn can_define_options_in_configfile() { let config_contents = r#" diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index cbbc9491fc..efaa0102e3 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -29,62 +29,6 @@ impl fmt::Debug for Keybinds { } } - -// pub struct Keybinds(HashMap); -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct ModeKeybinds(BTreeMap>); - -/// Intermediate struct used for deserialisation -/// Used in the config file. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct KeybindsFromYaml { - #[serde(flatten)] - keybinds: HashMap>, - #[serde(default)] - unbind: Unbind, -} - -/// Intermediate enum used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -#[serde(untagged)] -enum KeyActionUnbind { - KeyAction(KeyActionFromYaml), - Unbind(UnbindFromYaml), -} - -/// Intermediate struct used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize)] -struct KeyActionUnbindFromYaml { - keybinds: Vec, - unbind: Unbind, -} - -/// Intermediate struct used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct KeyActionFromYaml { - action: Vec, - key: Vec, -} - -/// Intermediate struct used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -struct UnbindFromYaml { - unbind: Unbind, -} - -/// List of keys, for which to disable their respective default actions -/// `All` is a catch all, and will disable the default actions for all keys. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] -#[serde(untagged)] -enum Unbind { - // This is the correct order, don't rearrange! - // Suspected Bug in the untagged macro. - // 1. Keys - Keys(Vec), - // 2. All - All(bool), -} - impl Keybinds { pub fn get_actions_for_key_in_mode(&self, mode: &InputMode, key: &Key) -> Option<&Vec> { self.0.get(mode) @@ -123,96 +67,6 @@ impl Keybinds { } } -impl ModeKeybinds { - fn new() -> ModeKeybinds { - ModeKeybinds(BTreeMap::>::new()) - } - - /// Merges `self` with `other`, if keys are the same, `other` overwrites. - fn merge(self, other: ModeKeybinds) -> ModeKeybinds { - let mut merged = self; - merged.0.extend(other.0); - merged - } - - /// Remove [`Key`]'s from [`ModeKeybinds`] - fn unbind_keys(self, unbind: Vec) -> Self { - let mut keymap = self; - for key in unbind { - keymap.0.remove(&key); - } - keymap - } - - pub fn to_cloned_vec(&self) -> Vec<(Key, Vec)> { - self.0 - .iter() - .map(|(key, vac)| (*key, vac.clone())) - .collect() - } -} - -// impl From for Keybinds { -// fn from(keybinds_from_yaml: KeybindsFromYaml) -> Keybinds { -// let mut keybinds = Keybinds::new(); -// -// for mode in InputMode::iter() { -// let mut mode_keybinds = ModeKeybinds::new(); -// if let Some(key_action) = keybinds_from_yaml.keybinds.get(&mode) { -// for keybind in key_action { -// mode_keybinds = mode_keybinds.merge(ModeKeybinds::from(keybind.clone())); -// } -// } -// keybinds.0.insert(mode, mode_keybinds); -// } -// keybinds -// } -// } - -/// For each [`Key`] assigned to [`Action`]s, -/// map the [`Action`]s to the [`Key`] -impl From for ModeKeybinds { - fn from(key_action: KeyActionFromYaml) -> ModeKeybinds { - let actions = key_action.action; - - ModeKeybinds( - key_action - .key - .into_iter() - .map(|k| (k, actions.clone())) - .collect::>>(), - ) - } -} - -impl From for ModeKeybinds { - fn from(key_action_unbind: KeyActionUnbind) -> ModeKeybinds { - match key_action_unbind { - KeyActionUnbind::KeyAction(key_action) => ModeKeybinds::from(key_action), - KeyActionUnbind::Unbind(_) => ModeKeybinds::new(), - } - } -} - -impl From> for ModeKeybinds { - fn from(key_action_from_yaml: Vec) -> ModeKeybinds { - let mut mode_keybinds = ModeKeybinds::new(); - - for keybind in key_action_from_yaml { - for key in keybind.key { - mode_keybinds.0.insert(key, keybind.action.clone()); - } - } - mode_keybinds - } -} - -impl Default for Unbind { - fn default() -> Unbind { - Unbind::All(false) - } -} - // The unit test location. #[cfg(test)] #[path = "./unit/keybinds_test.rs"] diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index 83ab967f86..5d659eb28a 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -21,17 +21,6 @@ use crate::setup; use std::fmt; use std::collections::BTreeMap; -// lazy_static! { -// static ref DEFAULT_CONFIG_PLUGINS: PluginsConfig = { -// let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec()).unwrap(); -// let cfg_yaml: ConfigFromYaml = serde_yaml::from_str(&cfg).unwrap(); -// PluginsConfig::try_from(cfg_yaml.plugins).unwrap() -// }; -// } - -// #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] -// pub struct PluginsConfigFromYaml(Vec); - /// Used in the config struct for plugin metadata #[derive(Clone, PartialEq, Deserialize, Serialize)] pub struct PluginsConfig(pub HashMap); @@ -90,36 +79,6 @@ impl Default for PluginsConfig { } } -// impl TryFrom for PluginsConfig { -// type Error = PluginsConfigError; -// -// fn try_from(yaml: PluginsConfigFromYaml) -> Result { -// let mut plugins = HashMap::new(); -// for plugin in yaml.0 { -// if plugins.contains_key(&plugin.tag) { -// return Err(PluginsConfigError::DuplicatePlugins(plugin.tag)); -// } -// plugins.insert(plugin.tag.clone(), plugin.into()); -// } -// -// Ok(PluginsConfig(plugins)) -// } -// } - -// impl From for PluginConfig { -// fn from(plugin: PluginConfigFromYaml) -> Self { -// PluginConfig { -// path: plugin.path, -// run: match plugin.run { -// PluginTypeFromYaml::Pane => PluginType::Pane(None), -// PluginTypeFromYaml::Headless => PluginType::Headless, -// }, -// _allow_exec_host_cmd: plugin._allow_exec_host_cmd, -// location: RunPluginLocation::Zellij(plugin.tag), -// } -// } -// } - /// Plugin metadata #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct PluginConfig { @@ -184,31 +143,6 @@ impl Default for PluginType { } } -// #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] -// pub struct PluginConfigFromYaml { -// pub path: PathBuf, -// pub tag: PluginTag, -// #[serde(default)] -// pub run: PluginTypeFromYaml, -// #[serde(default)] -// pub config: serde_yaml::Value, -// #[serde(default)] -// pub _allow_exec_host_cmd: bool, -// } - -// #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -// #[serde(rename_all = "kebab-case")] -// pub enum PluginTypeFromYaml { -// Headless, -// Pane, -// } - -// impl Default for PluginTypeFromYaml { -// fn default() -> Self { -// Self::Pane -// } -// } - #[derive(Error, Debug, PartialEq)] pub enum PluginsConfigError { #[error("Duplication in plugin tag names is not allowed: '{}'", String::from(.0.clone()))] @@ -218,101 +152,3 @@ pub enum PluginsConfigError { #[error("Could not find plugin at the path: '{0:?}'")] InvalidPluginLocation(PathBuf), } - -// TODO: make sure all these cases are tested -// -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::input::config::ConfigError; -// use std::convert::TryInto; -// -// #[test] -// fn run_plugin_permissions_are_inherited() -> Result<(), ConfigError> { -// let yaml_plugins: PluginsConfigFromYaml = serde_yaml::from_str( -// " -// - path: boo.wasm -// tag: boo -// _allow_exec_host_cmd: false -// ", -// )?; -// let plugins = PluginsConfig::try_from(yaml_plugins)?; -// -// assert_eq!( -// plugins.get(RunPlugin { -// _allow_exec_host_cmd: true, -// location: RunPluginLocation::Zellij(PluginTag::new("boo")) -// }), -// Some(PluginConfig { -// _allow_exec_host_cmd: true, -// path: PathBuf::from("boo.wasm"), -// location: RunPluginLocation::Zellij(PluginTag::new("boo")), -// run: PluginType::Pane(None), -// }) -// ); -// -// Ok(()) -// } -// -// #[test] -// fn try_from_yaml_fails_when_duplicate_tag_names_are_present() -> Result<(), ConfigError> { -// let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( -// " -// plugins: -// - path: /foo/bar/baz.wasm -// tag: boo -// - path: /foo/bar/boo.wasm -// tag: boo -// ", -// )?; -// -// assert_eq!( -// PluginsConfig::try_from(plugins), -// Err(PluginsConfigError::DuplicatePlugins(PluginTag::new("boo"))) -// ); -// -// Ok(()) -// } -// -// #[test] -// fn default_plugins() -> Result<(), ConfigError> { -// let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( -// " -// plugins: -// - path: boo.wasm -// tag: boo -// ", -// )?; -// let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?); -// -// assert_eq!(plugins.iter().count(), 5); -// Ok(()) -// } -// -// #[test] -// fn default_plugins_allow_overriding() -> Result<(), ConfigError> { -// let ConfigFromYaml { plugins, .. } = serde_yaml::from_str( -// " -// plugins: -// - path: boo.wasm -// tag: tab-bar -// ", -// )?; -// let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?); -// -// assert_eq!( -// plugins.get(RunPlugin { -// _allow_exec_host_cmd: false, -// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")) -// }), -// Some(PluginConfig { -// _allow_exec_host_cmd: false, -// path: PathBuf::from("boo.wasm"), -// location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// run: PluginType::Pane(None), -// }) -// ); -// -// Ok(()) -// } -// } diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index d665dae3c4..512098d9cc 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -4,17 +4,7 @@ use serde::{ }; use std::{collections::{HashMap, BTreeMap}, fmt}; -use kdl::KdlNode; -use crate::{entry_count, kdl_entries_as_i64, kdl_first_entry_as_string, kdl_first_entry_as_i64, kdl_children_or_error, kdl_name, kdl_children_nodes_or_error, kdl_get_child, kdl_children_property_first_arg_as_bool}; - -use super::options::Options; -use super::config::ConfigError; -use crate::data::{Palette, PaletteColor}; -use crate::shared::detect_theme_hue; - -/// Intermediate deserialization of themes -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct ThemesFromYaml(HashMap); +use crate::data::Palette; #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] pub struct UiConfig { @@ -80,32 +70,6 @@ pub struct Theme { pub palette: Palette, } -/// Intermediate deserialization struct -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] -pub struct PaletteFromYaml { - pub fg: PaletteColorFromYaml, - pub bg: PaletteColorFromYaml, - pub black: PaletteColorFromYaml, - pub red: PaletteColorFromYaml, - pub green: PaletteColorFromYaml, - pub yellow: PaletteColorFromYaml, - pub blue: PaletteColorFromYaml, - pub magenta: PaletteColorFromYaml, - pub cyan: PaletteColorFromYaml, - pub white: PaletteColorFromYaml, - pub orange: PaletteColorFromYaml, -} - -/// Intermediate deserialization enum -// This is here in order to make the untagged enum work -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(untagged)] -pub enum PaletteColorFromYaml { - Rgb((u8, u8, u8)), - EightBit(u8), - Hex(HexColor), -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct HexColor(u8, u8, u8); @@ -170,67 +134,6 @@ impl Serialize for HexColor { } } -impl Default for PaletteColorFromYaml { - fn default() -> Self { - PaletteColorFromYaml::EightBit(0) - } -} - -impl ThemesFromYaml { - pub fn theme_config(self, opts: &Options) -> Option { - let mut from_yaml = self; - match &opts.theme { - Some(theme) => from_yaml.from_default_theme(theme.to_owned()), - None => from_yaml.from_default_theme("default".into()), - } - } - - fn get_theme(&mut self, theme: String) -> Option { - self.0.remove(&theme) - } - - #[allow(clippy::wrong_self_convention)] - fn from_default_theme(&mut self, theme: String) -> Option { - self.clone() - .get_theme(theme) - .map(|t| t.palette) - } - - /// Merges two Theme structs into one Theme struct - /// `other` overrides the Theme of `self`. - pub fn merge(&self, other: Self) -> Self { - let mut theme = self.0.clone(); - theme.extend(other.0); - Self(theme) - } -} - -impl From for Palette { - fn from(yaml: PaletteFromYaml) -> Self { - Palette { - fg: yaml.fg.into(), - bg: yaml.bg.into(), - black: yaml.black.into(), - red: yaml.red.into(), - green: yaml.green.into(), - yellow: yaml.yellow.into(), - blue: yaml.blue.into(), - magenta: yaml.magenta.into(), - cyan: yaml.cyan.into(), - white: yaml.white.into(), - orange: yaml.orange.into(), - theme_hue: detect_theme_hue(yaml.bg.into()), - ..Palette::default() - } - } -} - -impl From for PaletteColor { - fn from(yaml: PaletteColorFromYaml) -> Self { - match yaml { - PaletteColorFromYaml::Rgb(color) => PaletteColor::Rgb(color), - PaletteColorFromYaml::EightBit(color) => PaletteColor::EightBit(color), - PaletteColorFromYaml::Hex(color) => PaletteColor::Rgb(color.into()), - } - } -} +#[cfg(test)] +#[path = "./unit/theme_test.rs"] +mod layout_test; diff --git a/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl b/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl new file mode 100644 index 0000000000..2201b73ac9 --- /dev/null +++ b/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl @@ -0,0 +1,16 @@ +// Dracula Theme + +dracula { + // From https://github.com/dracula/zellij + 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 + fg 248 248 242 + cyan 139 233 253 + black 0 0 0 + white 255 255 255 +} diff --git a/zellij-utils/src/input/unit/fixtures/themes/dracula.yaml b/zellij-utils/src/input/unit/fixtures/themes/dracula.yaml deleted file mode 100644 index b9c8a5afb7..0000000000 --- a/zellij-utils/src/input/unit/fixtures/themes/dracula.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Dracula Theme - -themes: - dracula: - # From https://github.com/dracula/zellij - 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] - fg: [248, 248, 242] - cyan: [139, 233, 253] - black: [0, 0, 0] - white: [255, 255, 255] diff --git a/zellij-utils/src/input/unit/fixtures/themes/nord.kdl b/zellij-utils/src/input/unit/fixtures/themes/nord.kdl new file mode 100644 index 0000000000..136b983992 --- /dev/null +++ b/zellij-utils/src/input/unit/fixtures/themes/nord.kdl @@ -0,0 +1,15 @@ +// Nord theme + +nord { + fg 216 222 233 //#D8DEE9 + bg 46 52 64 //#2E3440 + black 59 66 82 //#3B4252 + red 191 97 106 //#BF616A + green 163 190 140 //#A3BE8C + yellow 235203139 //#EBCB8B + blue 129 161 193 //#81A1C1 + magenta 180 142 173 //#B48EAD + cyan 136 192 208 //#88C0D0 + white 229 233 240 //#E5E9F0 + orange 208 135 112 //#D08770 +} diff --git a/zellij-utils/src/input/unit/fixtures/themes/nord.yaml b/zellij-utils/src/input/unit/fixtures/themes/nord.yaml deleted file mode 100644 index 61851ad201..0000000000 --- a/zellij-utils/src/input/unit/fixtures/themes/nord.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Nord theme - -themes: - nord: - fg: [216, 222, 233] #D8DEE9 - bg: [46, 52, 64] #2E3440 - black: [59, 66, 82] #3B4252 - red: [191, 97, 106] #BF616A - green: [163, 190, 140] #A3BE8C - yellow: [235,203,139] #EBCB8B - blue: [129, 161, 193] #81A1C1 - magenta: [180, 142, 173] #B48EAD - cyan: [136, 192, 208] #88C0D0 - white: [229, 233, 240] #E5E9F0 - orange: [208, 135, 112] #D08770 - diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index 3d133a1c10..1bc584d4e2 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -1,812 +1,465 @@ use super::super::actions::*; use super::super::keybinds::*; -use crate::data::{CharOrArrow, Key}; +use crate::input::config::Config; +use crate::data::{self, CharOrArrow, Key}; // TODO: make sure these are all covered +// TODO: move keybind tests here -// #[test] -// fn merge_keybinds_merges_different_keys() { -// let mut mode_keybinds_self = ModeKeybinds::new(); -// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); -// let mut mode_keybinds_other = ModeKeybinds::new(); -// mode_keybinds_other -// .0 -// .insert(Key::Backspace, vec![Action::NoOp]); -// -// let mut mode_keybinds_expected = ModeKeybinds::new(); -// mode_keybinds_expected -// .0 -// .insert(Key::F(1), vec![Action::NoOp]); -// mode_keybinds_expected -// .0 -// .insert(Key::Backspace, vec![Action::NoOp]); -// -// let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); -// -// assert_eq!(mode_keybinds_expected, mode_keybinds_merged); -// } -// -// #[test] -// fn merge_mode_keybinds_overwrites_same_keys() { -// let mut mode_keybinds_self = ModeKeybinds::new(); -// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); -// let mut mode_keybinds_other = ModeKeybinds::new(); -// mode_keybinds_other -// .0 -// .insert(Key::F(1), vec![Action::GoToTab(1)]); -// -// let mut mode_keybinds_expected = ModeKeybinds::new(); -// mode_keybinds_expected -// .0 -// .insert(Key::F(1), vec![Action::GoToTab(1)]); -// -// let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); -// -// assert_eq!(mode_keybinds_expected, mode_keybinds_merged); -// } -// -// #[test] -// fn merge_keybinds_merges() { -// let mut mode_keybinds_self = ModeKeybinds::new(); -// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); -// let mut mode_keybinds_other = ModeKeybinds::new(); -// mode_keybinds_other -// .0 -// .insert(Key::Backspace, vec![Action::NoOp]); -// let mut keybinds_self = Keybinds::new(); -// keybinds_self -// .0 -// .insert(InputMode::Normal, mode_keybinds_self.clone()); -// let mut keybinds_other = Keybinds::new(); -// keybinds_other -// .0 -// .insert(InputMode::Resize, mode_keybinds_other.clone()); -// let mut keybinds_expected = Keybinds::new(); -// keybinds_expected -// .0 -// .insert(InputMode::Normal, mode_keybinds_self); -// keybinds_expected -// .0 -// .insert(InputMode::Resize, mode_keybinds_other); -// -// assert_eq!( -// keybinds_expected, -// keybinds_self.merge_keybinds(keybinds_other) -// ) -// } -// -// #[test] -// fn merge_keybinds_overwrites_same_keys() { -// let mut mode_keybinds_self = ModeKeybinds::new(); -// mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); -// mode_keybinds_self.0.insert(Key::F(2), vec![Action::NoOp]); -// mode_keybinds_self.0.insert(Key::F(3), vec![Action::NoOp]); -// let mut mode_keybinds_other = ModeKeybinds::new(); -// mode_keybinds_other -// .0 -// .insert(Key::F(1), vec![Action::GoToTab(1)]); -// mode_keybinds_other -// .0 -// .insert(Key::F(2), vec![Action::GoToTab(2)]); -// mode_keybinds_other -// .0 -// .insert(Key::F(3), vec![Action::GoToTab(3)]); -// let mut keybinds_self = Keybinds::new(); -// keybinds_self -// .0 -// .insert(InputMode::Normal, mode_keybinds_self); -// let mut keybinds_other = Keybinds::new(); -// keybinds_other -// .0 -// .insert(InputMode::Normal, mode_keybinds_other.clone()); -// let mut keybinds_expected = Keybinds::new(); -// keybinds_expected -// .0 -// .insert(InputMode::Normal, mode_keybinds_other); -// -// assert_eq!( -// keybinds_expected, -// keybinds_self.merge_keybinds(keybinds_other) -// ) -// } -// -// #[test] -// fn from_keyaction_from_yaml_to_mode_keybindings() { -// let actions = vec![Action::NoOp, Action::GoToTab(1)]; -// let keyaction = KeyActionFromYaml { -// action: actions.clone(), -// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], -// }; -// -// let mut expected = ModeKeybinds::new(); -// expected.0.insert(Key::F(1), actions.clone()); -// expected.0.insert(Key::Backspace, actions.clone()); -// expected.0.insert(Key::Char('t'), actions); -// -// assert_eq!(expected, ModeKeybinds::from(keyaction)); -// } -// -// #[test] -// fn toplevel_unbind_unbinds_all() { -// let from_yaml = KeybindsFromYaml { -// unbind: Unbind::All(true), -// keybinds: HashMap::new(), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// assert_eq!(keybinds_from_yaml, Keybinds::new()); -// } -// -// #[test] -// fn no_unbind_unbinds_none() { -// let from_yaml = KeybindsFromYaml { -// unbind: Unbind::All(false), -// keybinds: HashMap::new(), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// assert_eq!(keybinds_from_yaml, Keybinds::default()); -// } -// -// #[test] -// fn last_keybind_is_taken() { -// let actions_1 = vec![Action::NoOp, Action::NewTab(None)]; -// let keyaction_1 = KeyActionFromYaml { -// action: actions_1, -// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], -// }; -// let actions_2 = vec![Action::GoToTab(1)]; -// let keyaction_2 = KeyActionFromYaml { -// action: actions_2.clone(), -// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], -// }; -// -// let mut expected = ModeKeybinds::new(); -// expected.0.insert(Key::F(1), actions_2.clone()); -// expected.0.insert(Key::Backspace, actions_2.clone()); -// expected.0.insert(Key::Char('t'), actions_2); -// -// assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); -// } -// -// #[test] -// fn last_keybind_overwrites() { -// let actions_1 = vec![Action::NoOp, Action::NewTab(None)]; -// let keyaction_1 = KeyActionFromYaml { -// action: actions_1.clone(), -// key: vec![Key::F(1), Key::Backspace, Key::Char('t')], -// }; -// let actions_2 = vec![Action::GoToTab(1)]; -// let keyaction_2 = KeyActionFromYaml { -// action: actions_2.clone(), -// key: vec![Key::F(1), Key::Char('t')], -// }; -// -// let mut expected = ModeKeybinds::new(); -// expected.0.insert(Key::F(1), actions_2.clone()); -// expected.0.insert(Key::Backspace, actions_1); -// expected.0.insert(Key::Char('t'), actions_2); -// -// assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); -// } -// -// #[test] -// fn unbind_single_mode() { -// let unbind = Unbind::All(true); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds); -// -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let result = keybinds.0.get(&InputMode::Normal); -// assert!(result.is_none()); -// } -// -// #[test] -// fn unbind_multiple_modes() { -// let unbind = Unbind::All(true); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds.clone()); -// keys.insert(InputMode::Pane, key_action_unbinds); -// -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let normal = keybinds.0.get(&InputMode::Normal); -// let pane = keybinds.0.get(&InputMode::Pane); -// assert!(normal.is_none()); -// assert!(pane.is_none()); -// } -// -// #[test] -// fn unbind_single_keybind_single_mode() { -// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds); -// -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let mode_keybinds = keybinds.0.get(&InputMode::Normal); -// let result = mode_keybinds -// .expect("Mode shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// assert!(result.is_none()); -// } -// -// #[test] -// fn unbind_single_keybind_multiple_modes() { -// let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); -// let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); -// let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; -// let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; -// let key_action_unbinds_n = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; -// let key_action_unbinds_h = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; -// -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds_n); -// keys.insert(InputMode::Pane, key_action_unbinds_h); -// -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let normal = keybinds.0.get(&InputMode::Normal); -// let pane = keybinds.0.get(&InputMode::Pane); -// let result_normal = normal -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_pane = pane -// .expect("Mode shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('h'))); -// assert!(result_normal.is_none()); -// assert!(result_pane.is_none()); -// } -// -// #[test] -// fn unbind_multiple_keybinds_single_mode() { -// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds); -// -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let mode_keybinds = keybinds.0.get(&InputMode::Normal); -// let result_n = mode_keybinds -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_p = mode_keybinds -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('p')); -// assert!(result_n.is_none()); -// assert!(result_p.is_none()); -// } -// -// #[test] -// fn unbind_multiple_keybinds_multiple_modes() { -// let unbind_normal = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); -// let unbind_resize = Unbind::Keys(vec![Key::Char('h'), Key::Ctrl('r')]); -// let unbind_from_yaml_normal = UnbindFromYaml { -// unbind: unbind_normal, -// }; -// let unbind_from_yaml_resize = UnbindFromYaml { -// unbind: unbind_resize, -// }; -// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; -// let key_action_unbinds_resize = vec![KeyActionUnbind::Unbind(unbind_from_yaml_resize)]; -// -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds_normal); -// keys.insert(InputMode::Resize, key_action_unbinds_resize); -// -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); -// let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); -// let result_normal_1 = mode_keybinds_normal -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_normal_2 = mode_keybinds_normal -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('p')); -// let result_resize_1 = mode_keybinds_resize -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Char('h')); -// let result_resize_2 = mode_keybinds_resize -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('r')); -// assert!(result_normal_1.is_none()); -// assert!(result_resize_1.is_none()); -// assert!(result_normal_2.is_none()); -// assert!(result_resize_2.is_none()); -// } -// -// #[test] -// fn unbind_multiple_keybinds_all_modes() { -// let unbind = Unbind::Keys(vec![ -// Key::Alt(CharOrArrow::Char('n')), -// Key::Alt(CharOrArrow::Char('h')), -// ]); -// let keys = HashMap::>::new(); -// let keybinds_from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind, -// }; -// -// let keybinds = Keybinds::unbind(keybinds_from_yaml); -// let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); -// let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); -// let result_normal_1 = mode_keybinds_normal -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_normal_2 = mode_keybinds_normal -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('f')); -// let result_resize_1 = mode_keybinds_resize -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Char('n')); -// let result_resize_2 = mode_keybinds_resize -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('f')); -// assert!(result_normal_1.is_none()); -// assert!(result_resize_1.is_none()); -// assert!(result_normal_2.is_none()); -// assert!(result_resize_2.is_none()); -// } -// -// #[test] -// fn unbind_all_toplevel_single_key_single_mode() { -// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds_normal); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(true), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// assert_eq!(keybinds_from_yaml, Keybinds::new()); -// } -// -// #[test] -// fn unbind_all_toplevel_single_key_multiple_modes() { -// let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); -// let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h'))]); -// let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; -// let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; -// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; -// let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds_normal); -// keys.insert(InputMode::Pane, key_action_unbinds_pane); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(true), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// assert_eq!(keybinds_from_yaml, Keybinds::new()); -// } -// -// #[test] -// fn unbind_all_toplevel_multiple_key_multiple_modes() { -// let unbind_n = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n')), Key::Ctrl('p')]); -// let unbind_h = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('h')), Key::Ctrl('t')]); -// let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; -// let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; -// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; -// let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds_normal); -// keys.insert(InputMode::Pane, key_action_unbinds_pane); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(true), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// assert_eq!(keybinds_from_yaml, Keybinds::new()); -// } -// -// #[test] -// fn unbind_all_toplevel_all_key_multiple_modes() { -// let unbind = Unbind::All(true); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml.clone())]; -// let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbinds_normal); -// keys.insert(InputMode::Pane, key_action_unbinds_pane); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(true), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// assert_eq!(keybinds_from_yaml, Keybinds::new()); -// } -// -// #[test] -// fn unbind_single_keybind_all_modes() { -// let keys = HashMap::>::new(); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// let result_normal = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_pane = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_resize = keybinds_from_yaml -// .0 -// .get(&InputMode::Resize) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_tab = keybinds_from_yaml -// .0 -// .get(&InputMode::Tab) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// -// assert!(result_normal.is_none()); -// assert!(result_pane.is_none()); -// assert!(result_resize.is_none()); -// assert!(result_tab.is_none()); -// } -// -// #[test] -// fn unbind_single_toplevel_single_key_single_mode_identical() { -// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbind); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// let result_normal = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_pane = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_resize = keybinds_from_yaml -// .0 -// .get(&InputMode::Resize) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_tab = keybinds_from_yaml -// .0 -// .get(&InputMode::Tab) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// -// assert!(result_normal.is_none()); -// assert!(result_pane.is_none()); -// assert!(result_resize.is_none()); -// assert!(result_tab.is_none()); -// } -// -// #[test] -// fn unbind_single_toplevel_single_key_single_mode_differing() { -// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l'))]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbind); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// let result_normal_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_normal_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// let result_resize_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Resize) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_resize_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Resize) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// -// assert!(result_normal_n.is_none()); -// assert!(result_normal_l.is_none()); -// assert!(result_resize_n.is_none()); -// assert!(result_resize_l.is_some()); -// } -// -// #[test] -// fn unbind_single_toplevel_single_key_multiple_modes() { -// let unbind = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l'))]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbind.clone()); -// keys.insert(InputMode::Pane, key_action_unbind); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// let result_normal_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_normal_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// let result_pane_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_pane_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// -// assert!(result_normal_n.is_none()); -// assert!(result_normal_l.is_none()); -// assert!(result_pane_n.is_none()); -// assert!(result_pane_l.is_none()); -// } -// -// #[test] -// fn unbind_single_toplevel_multiple_keys_single_mode() { -// let unbind = Unbind::Keys(vec![ -// Key::Alt(CharOrArrow::Char('l')), -// Key::Alt(CharOrArrow::Char('h')), -// Key::Alt(CharOrArrow::Char('j')), -// Key::Alt(CharOrArrow::Char('k')), -// ]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbind.clone()); -// keys.insert(InputMode::Pane, key_action_unbind); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// let result_normal_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_normal_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// let result_normal_k = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('k'))); -// let result_normal_h = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('h'))); -// -// assert!(result_normal_n.is_none()); -// assert!(result_normal_l.is_none()); -// assert!(result_normal_h.is_none()); -// assert!(result_normal_k.is_none()); -// } -// -// #[test] -// fn unbind_single_toplevel_multiple_keys_multiple_modes() { -// let unbind_normal = Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('l')), Key::Ctrl('p')]); -// let unbind_from_yaml_normal = UnbindFromYaml { -// unbind: unbind_normal, -// }; -// let key_action_unbind_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; -// let unbind = Unbind::Keys(vec![ -// Key::Alt(CharOrArrow::Char('l')), -// Key::Alt(CharOrArrow::Char('k')), -// ]); -// let unbind_from_yaml = UnbindFromYaml { unbind }; -// let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbind_normal); -// keys.insert(InputMode::Pane, key_action_unbind); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::Keys(vec![Key::Alt(CharOrArrow::Char('n'))]), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// -// let result_normal_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_normal_p = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('p')); -// let result_normal_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// let result_pane_p = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Ctrl('p')); -// let result_pane_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('n'))); -// let result_pane_l = keybinds_from_yaml -// .0 -// .get(&InputMode::Pane) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Alt(CharOrArrow::Char('l'))); -// -// assert!(result_normal_n.is_none()); -// assert!(result_normal_l.is_none()); -// assert!(result_normal_p.is_none()); -// assert!(result_pane_n.is_none()); -// assert!(result_pane_p.is_some()); -// assert!(result_pane_l.is_none()); -// } -// -// #[test] -// fn uppercase_and_lowercase_are_distinct() { -// let key_action_n = KeyActionFromYaml { -// key: vec![Key::Char('n')], -// action: vec![Action::NewTab(None)], -// }; -// let key_action_large_n = KeyActionFromYaml { -// key: vec![Key::Char('N')], -// action: vec![Action::NewPane(None)], -// }; -// -// let key_action_unbind = vec![ -// KeyActionUnbind::KeyAction(key_action_n), -// KeyActionUnbind::KeyAction(key_action_large_n), -// ]; -// let mut keys = HashMap::>::new(); -// keys.insert(InputMode::Normal, key_action_unbind); -// let from_yaml = KeybindsFromYaml { -// keybinds: keys, -// unbind: Unbind::All(false), -// }; -// -// let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); -// let result_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Char('n')); -// let result_large_n = keybinds_from_yaml -// .0 -// .get(&InputMode::Normal) -// .expect("ModeKeybinds shouldn't be empty") -// .0 -// .get(&Key::Char('N')); -// -// assert!(result_n.is_some()); -// assert!(result_large_n.is_some()); -// } +#[test] +fn can_define_keybindings_in_configfile() { + let config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); + assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding successfully defined in config"); +} + +#[test] +fn can_define_multiple_keybinds_for_same_action() { + let config_contents = r#" + keybinds { + normal { + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let alt_h_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Alt(CharOrArrow::Direction(data::Direction::Left)) + ); + let alt_left_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Alt(CharOrArrow::Char('h')) + ); + assert_eq!(alt_h_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "First keybinding successfully defined in config"); + assert_eq!(alt_left_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "Second keybinding successfully defined in config"); +} + +#[test] +fn can_define_series_of_actions_for_same_keybinding() { + let config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Action series successfully defined"); +} + +#[test] +fn keybindings_bind_order_is_preserved() { + let config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Second keybinding was applied"); +} + +#[test] +fn uppercase_and_lowercase_keybindings_are_distinct() { + let config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "Z" { SwitchToMode "Resize"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let uppercase_z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('Z'), + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Lowercase z successfully bound"); + assert_eq!(uppercase_z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Uppercase z successfully bound"); +} + +#[test] +fn can_override_keybindings() { + let default_config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds { + pane { + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from config overrode keybinding from default config"); +} + +#[test] +fn can_add_to_default_keybindings() { + // this test just makes sure keybindings defined in a custom config are added to different + // keybindings defined in the default config + let default_config_contents = r#" + keybinds { + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds { + pane { + bind "r" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Keybinding from default config bound"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from custom config bound as well"); +} + +#[test] +fn can_clear_default_keybindings() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds clear-defaults=true { + normal { + bind "Ctrl r" { SwitchToMode "Locked"; } + } + pane { + bind "r" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let ctrl_r_in_normal_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Ctrl('r'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + assert_eq!(ctrl_g_normal_mode_action, None, "Keybinding from normal mode in default config cleared"); + assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); + assert_eq!(ctrl_r_in_normal_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding from normal mode in custom config still bound"); +} + +#[test] +fn can_clear_default_keybindings_per_single_mode() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + } + } + "#; + let config_contents = r#" + keybinds { + pane clear-defaults=true { + bind "r" { SwitchToMode "Resize"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Ctrl('g'), + ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode from default config not cleared"); + assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); +} + +#[test] +fn can_unbind_multiple_keys_globally() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "r" { TogglePaneFrames; } + } + } + "#; + let config_contents = r#" + keybinds { + unbind "Ctrl g" "z" + pane { + bind "t" { SwitchToMode "Tab"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Ctrl('g'), + ); + let ctrl_g_pane_mode_action = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Ctrl('g'), + ); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let t_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('t'), + ); + assert_eq!(ctrl_g_normal_mode_action, None, "First keybind uncleared in one mode"); + assert_eq!(ctrl_g_pane_mode_action, None, "First keybind uncleared in another mode"); + assert_eq!(z_in_pane_mode, None, "Second keybind cleared as well"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); + assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); +} + +#[test] +fn can_unbind_multiple_keys_per_single_mode() { + let default_config_contents = r#" + keybinds { + normal { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + pane { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "z" { TogglePaneFrames; SwitchToMode "Normal"; } + bind "r" { TogglePaneFrames; } + } + } + "#; + let config_contents = r#" + keybinds { + pane { + unbind "Ctrl g" "z" + bind "t" { SwitchToMode "Tab"; } + } + } + "#; + let default_config = Config::from_kdl(default_config_contents, None).unwrap(); + let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); + let ctrl_g_normal_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); + let ctrl_g_pane_mode_action = config + .keybinds + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g')); + let r_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('r'), + ); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + let t_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('t'), + ); + assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode not cleared"); + assert_eq!(ctrl_g_pane_mode_action, None, "First Keybind cleared in its mode"); + assert_eq!(z_in_pane_mode, None, "Second keybind cleared in its mode as well"); + assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); + assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); +} + +#[test] +fn can_define_shared_keybinds_for_all_modes() { + let config_contents = r#" + keybinds { + shared { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + for mode in InputMode::iter() { + let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); + } +} + +#[test] +fn can_define_shared_keybinds_with_exclusion() { + let config_contents = r#" + keybinds { + shared_except "locked" { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + for mode in InputMode::iter() { + let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + if mode == InputMode::Locked { + assert_eq!(action_in_mode, None, "Keybind unbound in excluded mode"); + } else { + assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); + } + } +} + +#[test] +fn can_define_shared_keybinds_with_inclusion() { + let config_contents = r#" + keybinds { + shared_among "normal" "resize" "pane" { + bind "Ctrl g" { SwitchToMode "Locked"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + for mode in InputMode::iter() { + let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + if mode == InputMode::Normal || mode == InputMode::Resize || mode == InputMode::Pane { + assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in included mode"); + } else { + assert_eq!(action_in_mode, None, "Keybind unbound in other modes"); + } + } +} + +#[test] +fn keybindings_unbinds_happen_after_binds() { + let config_contents = r#" + keybinds { + pane { + unbind "z" + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let config = Config::from_kdl(config_contents, None).unwrap(); + let z_in_pane_mode = config + .keybinds + .get_actions_for_key_in_mode( + &InputMode::Pane, + &Key::Char('z'), + ); + assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound"); +} diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 66868ec21f..ea0529c8f4 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -945,883 +945,4 @@ fn cannot_define_pane_template_names_as_keywords() { let layout = Layout::from_kdl(&kdl_layout); assert!(layout.is_err(), "{}", format!("error provided for pane template name with keyword: {}", keyword)); } - // TODO: CONTINUE HERE (28/08) - // - write this test - DONE - // - implement and write the rest of the tests below - DONE - // - refactor the kdl layouts part <-- YES! LET'S DO THIS! - // - add config and config overriding to layouts - } - -// TODO more tests: -// - tab_templates with pane templates - DONE -// - nested pane templates - DONE -// - children not on first level of template - DONE -// -// errors: -// - template names with spaces - DONE -// - default tab template with name - N/A -// - panes and tabs on same level - DONE -// - cannot define template names that are identical to keywords - DONE - -// // TODO: -// // - session name - added to the config, TODO (CONTINUE HERE 19/08): add test for it and for -// // attach_to_session in the config - DONE -// // - focus for pane/tab -// // - other stuff in layouts -// // - merge layouts with config -// // - default layout files -// // - open new tab with layout template (maybe in tab_integration_tests?) -// // - empty layout -// -// // TODO: CONTINUE HERE (18/08) -// // - write tests similar to the config that will feed KDL into Layout::from_kdl and assert stuff -// // about the layout - DONE -// // - then bring these tests back -// // TODO: BRING THESE TESTS BACK!! -// // -// // -// // #[test] -// // fn default_layout_is_ok() { -// // let path = default_layout_dir("default.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // assert!(layout.is_ok()); -// // } -// // -// // #[test] -// // fn default_layout_has_one_tab() { -// // let path = default_layout_dir("default.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // assert_eq!(layout_template.tabs.len(), 1); -// // } -// // -// // #[test] -// // fn default_layout_merged_correctly() { -// // let path = default_layout_dir("default.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: true, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(1)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: true, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(2)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn default_layout_new_tab_correct() { -// // let path = default_layout_dir("default.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: true, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(1)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: true, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(2)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn default_strider_layout_is_ok() { -// // let path = default_layout_dir("strider.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // assert!(layout_from_yaml.is_ok()); -// // } -// // -// // #[test] -// // fn default_disable_status_layout_is_ok() { -// // let path = default_layout_dir("disable-status-bar.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // assert!(layout_from_yaml.is_ok()); -// // } -// // -// // #[test] -// // fn default_disable_status_layout_has_no_tab() { -// // let path = default_layout_dir("disable-status-bar.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // assert_eq!(layout_template.tabs.len(), 0); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_is_ok() { -// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // assert!(layout.is_ok()); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_has_one_tab() { -// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.unwrap(); -// // assert_eq!(layout_template.tabs.len(), 1); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_merged_correctly() { -// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_new_tab_is_correct() { -// // let path = layout_test_dir("three-panes-with-tab.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_and_default_plugins_is_ok() { -// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // assert!(layout.is_ok()); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_and_default_plugins_has_one_tab() { -// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.unwrap(); -// // assert_eq!(layout_template.tabs.len(), 1); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_and_default_plugins_merged_correctly() { -// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(1)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(2)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { -// // let path = layout_test_dir("three-panes-with-tab-and-default-plugins.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(1)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Fixed(2)), -// // run: Some(Run::Plugin(RunPlugin { -// // location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), -// // _allow_exec_host_cmd: false, -// // })), -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn deeply_nested_tab_is_ok() { -// // let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // assert!(layout.is_ok()); -// // } -// // -// // #[test] -// // fn deeply_nested_tab_has_one_tab() { -// // let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.unwrap(); -// // assert_eq!(layout_template.tabs.len(), 1); -// // } -// // -// // #[test] -// // fn deeply_nested_tab_merged_correctly() { -// // let path = layout_test_dir("deeply-nested-tab-layout.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(21)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(22)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(23)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(24)), -// // run: None, -// // }, -// // ], -// // split_size: Some(SplitSize::Percent(78)), -// // run: None, -// // }, -// // ], -// // split_size: Some(SplitSize::Percent(79)), -// // run: None, -// // }, -// // ], -// // split_size: Some(SplitSize::Percent(90)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(15)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(15)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(15)), -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn three_tabs_is_ok() { -// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // assert!(layout_from_yaml.is_ok()); -// // } -// // -// // #[test] -// // fn three_tabs_has_three_tabs() { -// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.unwrap(); -// // assert_eq!(layout_template.tabs.len(), 3); -// // } -// // -// // #[test] -// // fn three_tabs_tab_one_merged_correctly() { -// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn three_tabs_tab_two_merged_correctly() { -// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[1].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn three_tabs_tab_three_merged_correctly() { -// // let path = layout_test_dir("three-tabs-merged-correctly.yaml".into()); -// // let layout = LayoutFromYaml::new(&path); -// // let layout_template = layout.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[2].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![ -// // Layout { -// // direction: Direction::Vertical, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: Some(SplitSize::Percent(50)), -// // run: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // }; -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn no_tabs_is_ok() { -// // let path = layout_test_dir("no-tab-section-specified.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // assert!(layout_from_yaml.is_ok()); -// // } -// // -// // #[test] -// // fn no_tabs_has_no_tabs() { -// // let path = layout_test_dir("no-tab-section-specified.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.unwrap(); -// // assert_eq!(layout_template.tabs.len(), 0); -// // } -// // -// // #[test] -// // fn no_tabs_merged_correctly() { -// // let path = layout_test_dir("no-tab-section-specified.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // let tab_layout = layout_template.template.clone().insert_tab_layout(None); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![Layout { -// // direction: Direction::Horizontal, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // }], -// // split_size: None, -// // run: None, -// // }; -// // -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn no_layout_template_specified_is_ok() { -// // let path = layout_test_dir("no-layout-template-specified.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // assert!(layout_from_yaml.is_ok()); -// // } -// // -// // #[test] -// // fn no_layout_template_has_one_tab() { -// // let path = layout_test_dir("no-layout-template-specified.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.unwrap(); -// // assert_eq!(layout_template.tabs.len(), 1); -// // } -// // -// // #[test] -// // fn no_layout_template_merged_correctly() { -// // let path = layout_test_dir("no-layout-template-specified.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.as_ref().unwrap(); -// // let tab_layout = layout_template -// // .template -// // .clone() -// // .insert_tab_layout(Some(layout_template.tabs[0].clone())); -// // let merged_layout = Layout { -// // direction: Direction::Horizontal, -// // parts: vec![Layout { -// // direction: Direction::Vertical, -// // parts: vec![ -// // Layout { -// // direction: Direction::Horizontal, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // }, -// // Layout { -// // direction: Direction::Horizontal, -// // parts: vec![], -// // split_size: None, -// // run: None, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // }, -// // ], -// // split_size: None, -// // run: None, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // }], -// // split_size: None, -// // run: None, -// // borderless: false, -// // pane_name: None, -// // focus: None, -// // }; -// // -// // assert_eq!(merged_layout, tab_layout.try_into().unwrap()); -// // } -// // -// // #[test] -// // fn session_name_to_layout_is_ok() { -// // let path = layout_test_dir("session-name-to-layout.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // assert!(layout_from_yaml.is_ok()); -// // } -// // -// // #[test] -// // fn session_name_to_layout_has_name() { -// // let path = layout_test_dir("session-name-to-layout.yaml".into()); -// // let layout_from_yaml = LayoutFromYaml::new(&path); -// // let layout_template = layout_from_yaml.unwrap(); -// // let session_layout = layout_template.session; -// // -// // let expected_session = SessionFromYaml { -// // name: Some(String::from("zellij-session")), -// // attach: Some(true), -// // }; -// // -// // assert_eq!(expected_session, session_layout); -// // } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__layout_test__dracula_theme_from_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__layout_test__dracula_theme_from_file.snap new file mode 100644 index 0000000000..26ee77f898 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__layout_test__dracula_theme_from_file.snap @@ -0,0 +1,109 @@ +--- +source: zellij-utils/src/input/./unit/theme_test.rs +assertion_line: 15 +expression: "format!(\"{:#?}\", theme)" +--- +( + "dracula", + Theme { + palette: Palette { + source: Default, + theme_hue: Dark, + fg: Rgb( + ( + 248, + 248, + 242, + ), + ), + bg: Rgb( + ( + 40, + 42, + 54, + ), + ), + black: Rgb( + ( + 0, + 0, + 0, + ), + ), + red: Rgb( + ( + 255, + 85, + 85, + ), + ), + green: Rgb( + ( + 80, + 250, + 123, + ), + ), + yellow: Rgb( + ( + 241, + 250, + 140, + ), + ), + blue: Rgb( + ( + 98, + 114, + 164, + ), + ), + magenta: Rgb( + ( + 255, + 121, + 198, + ), + ), + cyan: Rgb( + ( + 139, + 233, + 253, + ), + ), + white: Rgb( + ( + 255, + 255, + 255, + ), + ), + orange: Rgb( + ( + 255, + 184, + 108, + ), + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + }, +) diff --git a/zellij-utils/src/input/unit/theme_test.rs b/zellij-utils/src/input/unit/theme_test.rs index ad5b480af5..df4d6e3d9f 100644 --- a/zellij-utils/src/input/unit/theme_test.rs +++ b/zellij-utils/src/input/unit/theme_test.rs @@ -1,5 +1,6 @@ use super::super::theme::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use insta::assert_snapshot; fn theme_test_dir(theme: String) -> PathBuf { let root = Path::new(env!("CARGO_MANIFEST_DIR")); @@ -8,15 +9,15 @@ fn theme_test_dir(theme: String) -> PathBuf { } #[test] -fn dracula_theme_is_ok() { - let path = theme_test_dir("dracula.yaml".into()); - let theme = ThemesFromYaml::from_path(&path); - assert!(theme.is_ok()); +fn dracula_theme_from_file() { + let path = theme_test_dir("dracula.kdl".into()); + let theme = Theme::from_path(path).unwrap(); + assert_snapshot!(format!("{:#?}", theme)); } #[test] fn no_theme_is_err() { - let path = theme_test_dir("nonexistent.yaml".into()); - let theme = ThemesFromYaml::from_path(&path); + let path = theme_test_dir("nonexistent.kdl".into()); + let theme = Theme::from_path(path); assert!(theme.is_err()); } diff --git a/zellij-utils/src/ipc.rs b/zellij-utils/src/ipc.rs index 308e9979e4..2b7550d38a 100644 --- a/zellij-utils/src/ipc.rs +++ b/zellij-utils/src/ipc.rs @@ -85,7 +85,6 @@ pub enum ClientToServerMsg { ClientAttributes, Box, Box, - // Box, Box, Option, ), From 0223871687e4e2781e91ca05f748da4833aa04ac Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 8 Sep 2022 15:58:32 +0200 Subject: [PATCH 26/55] fix(tests): make e2e tests pass --- .../status-bar/src/tip/data/quicknav.rs | 5 +- src/tests/e2e/cases.rs | 142 +++++++++--------- ...j__tests__e2e__cases__bracketed_paste.snap | 4 +- ...zellij__tests__e2e__cases__close_pane.snap | 2 +- .../zellij__tests__e2e__cases__close_tab.snap | 2 +- ...e2e__cases__detach_and_attach_session.snap | 2 +- ...ts__e2e__cases__focus_pane_with_mouse.snap | 4 +- ..._tests__e2e__cases__mirrored_sessions.snap | 4 +- ...ers_in_different_panes_and_same_tab-2.snap | 4 +- ...users_in_different_panes_and_same_tab.snap | 4 +- ...s__multiple_users_in_different_tabs-2.snap | 4 +- ...ses__multiple_users_in_different_tabs.snap | 4 +- ...multiple_users_in_same_pane_and_tab-2.snap | 4 +- ...__multiple_users_in_same_pane_and_tab.snap | 4 +- ...llij__tests__e2e__cases__open_new_tab.snap | 2 +- ...ellij__tests__e2e__cases__resize_pane.snap | 2 +- ...s__e2e__cases__resize_terminal_window.snap | 4 +- ...s__scrolling_inside_a_pane_with_mouse.snap | 4 +- ...2e__cases__split_terminals_vertically.snap | 2 +- ...e2e__cases__start_without_pane_frames.snap | 4 +- ..._e2e__cases__starts_with_one_terminal.snap | 2 +- .../zellij__tests__e2e__cases__tmux_mode.snap | 4 +- ...__e2e__cases__typing_exit_closes_pane.snap | 2 +- ...__tests__e2e__cases__undo_rename_pane.snap | 4 +- ...j__tests__e2e__cases__undo_rename_tab.snap | 4 +- src/tests/fixtures/configs/changed_keys.kdl | 12 ++ src/tests/fixtures/configs/changed_keys.yaml | 26 ---- .../fixtures/layouts/focus-tab-layout.yaml | 92 ------------ .../parts-total-less-than-100-percent.yaml | 19 --- .../parts-total-more-than-100-percent.yaml | 20 --- .../layouts/three-panes-with-nesting.yaml | 17 --- zellij-utils/src/input/config.rs | 1 - .../layouts/deeply-nested-tab-layout.yaml | 41 ----- .../multiple-tabs-should-not-error.yaml | 18 --- .../layouts/no-layout-template-specified.yaml | 6 - .../layouts/no-tab-section-specified.yaml | 6 - .../layouts/session-name-to-layout.yaml | 3 - .../three-panes-with-tab-and-command.yaml | 35 ----- ...ee-panes-with-tab-and-default-plugins.yaml | 32 ---- .../layouts/three-panes-with-tab.yaml | 21 --- .../layouts/three-tabs-merged-correctly.yaml | 29 ---- zellij-utils/src/input/unit/keybinds_test.rs | 3 - 42 files changed, 125 insertions(+), 479 deletions(-) create mode 100644 src/tests/fixtures/configs/changed_keys.kdl delete mode 100644 src/tests/fixtures/configs/changed_keys.yaml delete mode 100644 src/tests/fixtures/layouts/focus-tab-layout.yaml delete mode 100644 src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml delete mode 100644 src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml delete mode 100644 src/tests/fixtures/layouts/three-panes-with-nesting.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/no-layout-template-specified.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/no-tab-section-specified.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/session-name-to-layout.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml delete mode 100644 zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml diff --git a/default-plugins/status-bar/src/tip/data/quicknav.rs b/default-plugins/status-bar/src/tip/data/quicknav.rs index 59b604a701..ca3fd376bc 100644 --- a/default-plugins/status-bar/src/tip/data/quicknav.rs +++ b/default-plugins/status-bar/src/tip/data/quicknav.rs @@ -72,13 +72,16 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups { style_key_with_modifier(&new_pane_keys, &help.style.colors) }; - let resize_keys = action_key_group( + let mut resize_keys = action_key_group( &normal_keymap, &[ &[Action::Resize(ResizeDirection::Increase)], &[Action::Resize(ResizeDirection::Decrease)], ], ); + if resize_keys.contains(&Key::Alt(CharOrArrow::Char('='))) && resize_keys.contains(&Key::Alt(CharOrArrow::Char('+'))) { + resize_keys.retain(|k| k != &Key::Alt(CharOrArrow::Char('='))); + } let resize = if resize_keys.is_empty() { vec![Style::new().bold().paint("UNBOUND")] } else { diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 7457ed544c..c2975d168f 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -948,40 +948,40 @@ pub fn detach_and_attach_session() { assert_snapshot!(last_snapshot); } -#[test] -#[ignore] -pub fn accepts_basic_layout() { - let fake_win_size = Size { - cols: 120, - rows: 24, - }; - let layout_file_name = "three-panes-with-nesting.yaml"; - let mut test_attempts = 10; - let last_snapshot = loop { - RemoteRunner::kill_running_sessions(fake_win_size); - let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); - runner.run_all_steps(); - let last_snapshot = runner.take_snapshot_after(Step { - name: "Wait for app to load", - instruction: |remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 1) - && remote_terminal.snapshot_contains("$ █ ││$") - && remote_terminal.snapshot_contains("$ ") { - step_is_complete = true; - } - step_is_complete - }, - }); - if runner.test_timed_out && test_attempts > 0 { - test_attempts -= 1; - continue; - } else { - break last_snapshot; - } - }; - assert_snapshot!(last_snapshot); -} +// #[test] +// #[ignore] +// pub fn accepts_basic_layout() { +// let fake_win_size = Size { +// cols: 120, +// rows: 24, +// }; +// let layout_file_name = "three-panes-with-nesting.yaml"; +// let mut test_attempts = 10; +// let last_snapshot = loop { +// RemoteRunner::kill_running_sessions(fake_win_size); +// let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); +// runner.run_all_steps(); +// let last_snapshot = runner.take_snapshot_after(Step { +// name: "Wait for app to load", +// instruction: |remote_terminal: RemoteTerminal| -> bool { +// let mut step_is_complete = false; +// if remote_terminal.cursor_position_is(3, 1) +// && remote_terminal.snapshot_contains("$ █ ││$") +// && remote_terminal.snapshot_contains("$ ") { +// step_is_complete = true; +// } +// step_is_complete +// }, +// }); +// if runner.test_timed_out && test_attempts > 0 { +// test_attempts -= 1; +// continue; +// } else { +// break last_snapshot; +// } +// }; +// assert_snapshot!(last_snapshot); +// } #[test] #[ignore] @@ -990,7 +990,7 @@ pub fn status_bar_loads_custom_keybindings() { cols: 120, rows: 24, }; - let config_file_name = "changed_keys.yaml"; + let config_file_name = "changed_keys.kdl"; let mut test_attempts = 10; let last_snapshot = loop { RemoteRunner::kill_running_sessions(fake_win_size); @@ -1718,42 +1718,42 @@ pub fn toggle_floating_panes() { assert_snapshot!(last_snapshot); } -#[test] -#[ignore] -pub fn focus_tab_with_layout() { - let fake_win_size = Size { - cols: 120, - rows: 24, - }; - let layout_file_name = "focus-tab-layout.yaml"; - let mut test_attempts = 10; - let last_snapshot = loop { - RemoteRunner::kill_running_sessions(fake_win_size); - let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); - runner.run_all_steps(); - let last_snapshot = runner.take_snapshot_after(Step { - name: "Wait for app to load", - instruction: |remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() - && remote_terminal.tip_appears() - && remote_terminal.snapshot_contains("Tab #9") - && remote_terminal.cursor_position_is(63, 2) - { - step_is_complete = true; - } - step_is_complete - }, - }); - if runner.test_timed_out && test_attempts > 0 { - test_attempts -= 1; - continue; - } else { - break last_snapshot; - } - }; - assert_snapshot!(last_snapshot); -} +// #[test] +// #[ignore] +// pub fn focus_tab_with_layout() { +// let fake_win_size = Size { +// cols: 120, +// rows: 24, +// }; +// let layout_file_name = "focus-tab-layout.yaml"; +// let mut test_attempts = 10; +// let last_snapshot = loop { +// RemoteRunner::kill_running_sessions(fake_win_size); +// let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); +// runner.run_all_steps(); +// let last_snapshot = runner.take_snapshot_after(Step { +// name: "Wait for app to load", +// instruction: |remote_terminal: RemoteTerminal| -> bool { +// let mut step_is_complete = false; +// if remote_terminal.status_bar_appears() +// && remote_terminal.tip_appears() +// && remote_terminal.snapshot_contains("Tab #9") +// && remote_terminal.cursor_position_is(63, 2) +// { +// step_is_complete = true; +// } +// step_is_complete +// }, +// }); +// if runner.test_timed_out && test_attempts > 0 { +// test_attempts -= 1; +// continue; +// } else { +// break last_snapshot; +// } +// }; +// assert_snapshot!(last_snapshot); +// } #[test] #[ignore] diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap index 8b66d5ace2..dcb76b9e14 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1636 +assertion_line: 1671 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap index 0d431c3292..ac55a9d4ad 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap index 603432fdfe..5f757fb657 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap index 92d2a0378c..5f79887ffa 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap index ef923aa2ed..d373cbbb1f 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1042 +assertion_line: 1077 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap index 68965f48e9..3a4212903c 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1307 +assertion_line: 1342 expression: first_runner_snapshot --- Zellij (mirrored_sessions)  Tab #1  Tab #2  @@ -26,4 +26,4 @@ expression: first_runner_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap index ffcb6b2fd0..5bf9756781 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1486 +assertion_line: 1521 expression: second_runner_snapshot --- Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ] @@ -26,4 +26,4 @@ expression: second_runner_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap index 797448a6a1..2316cf55fc 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1485 +assertion_line: 1520 expression: first_runner_snapshot --- Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ] @@ -26,4 +26,4 @@ expression: first_runner_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap index cd4006d5b6..d3c93217d1 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1581 +assertion_line: 1616 expression: second_runner_snapshot --- Zellij (multiple_users_in_different_tabs)  Tab #1 [ ] Tab #2  @@ -26,4 +26,4 @@ expression: second_runner_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap index 8856f1f9f3..86e8023da3 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1580 +assertion_line: 1615 expression: first_runner_snapshot --- Zellij (multiple_users_in_different_tabs)  Tab #1  Tab #2 [ ] @@ -26,4 +26,4 @@ expression: first_runner_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap index d7328b8db3..161f8eeaac 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1396 +assertion_line: 1431 expression: second_runner_snapshot --- Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ] @@ -26,4 +26,4 @@ expression: second_runner_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap index 6bc0369c93..15cc156aa1 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1395 +assertion_line: 1430 expression: first_runner_snapshot --- Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ] @@ -26,4 +26,4 @@ expression: first_runner_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap index 2d84b3f39c..2aa8011511 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap index f03656301c..c4dd1fff74 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap index de1ef639a1..d7bf8cd5c6 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 883 +assertion_line: 863 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ Ctrl + g  p  t  n  h  s  o  q  - QuickNav: Alt + / Alt + <←↓↑→> or Alt + / Alt + <+|=|-> + QuickNav: Alt + / Alt + <←↓↑→> or Alt + / Alt + <+|-> diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap index ec416eeab4..e5132b9f7d 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1117 +assertion_line: 1152 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ ││li█e19 │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap index 22b796279e..8dc5897b7c 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap index f066d20782..0fe416cf8c 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1163 +assertion_line: 1198 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ $ │$ █ │ │ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap index 1f4ff4f02d..6f2822c5f0 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap index 05985a5f27..e812fda8a0 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1767 +assertion_line: 1802 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap index 3a1643b16d..9fdd0f2dae 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap index 03b44ebb1e..e8c2771722 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1914 +assertion_line: 1949 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap index ed9a8e20e8..56b417d760 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1865 +assertion_line: 1900 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|=|-> => resize pane. + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/fixtures/configs/changed_keys.kdl b/src/tests/fixtures/configs/changed_keys.kdl new file mode 100644 index 0000000000..6123b1d5be --- /dev/null +++ b/src/tests/fixtures/configs/changed_keys.kdl @@ -0,0 +1,12 @@ +keybinds clear-defaults=true { + normal { + bind "F1" { SwitchToMode "Locked"; } + bind "F2" { SwitchToMode "Pane"; } + bind "F3" { SwitchToMode "Tab"; } + bind "F4" { SwitchToMode "Resize"; } + bind "F5" { SwitchToMode "Move"; } + bind "F6" { SwitchToMode "Scroll"; } + bind "F7" { SwitchToMode "Session"; } + bind "F8" { Quit; } + } +} diff --git a/src/tests/fixtures/configs/changed_keys.yaml b/src/tests/fixtures/configs/changed_keys.yaml deleted file mode 100644 index 7d2a8c714d..0000000000 --- a/src/tests/fixtures/configs/changed_keys.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -# Configuration for zellij. - -# In order to troubleshoot your configuration try using the following command: -# `zellij setup --check` -# It should show current config locations and features that are enabled. - -keybinds: - unbind: true - normal: - - action: [SwitchToMode: Locked,] - key: [F: 1] - - action: [SwitchToMode: Pane,] - key: [F: 2] - - action: [SwitchToMode: Tab,] - key: [F: 3] - - action: [SwitchToMode: Resize,] - key: [F: 4] - - action: [SwitchToMode: Move,] - key: [F: 5] - - action: [SwitchToMode: Scroll,] - key: [F: 6] - - action: [SwitchToMode: Session,] - key: [F: 7] - - action: [Quit,] - key: [F: 8] diff --git a/src/tests/fixtures/layouts/focus-tab-layout.yaml b/src/tests/fixtures/layouts/focus-tab-layout.yaml deleted file mode 100644 index 1d01902047..0000000000 --- a/src/tests/fixtures/layouts/focus-tab-layout.yaml +++ /dev/null @@ -1,92 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - borderless: true - - direction: Vertical - body: true - - direction: Vertical - split_size: - Fixed: 2 - run: - plugin: - location: "zellij:status-bar" - borderless: true - -tabs: -- direction: Vertical - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 -- direction: Vertical -- direction: Vertical - focus: true - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - focus: true - split_size: - Percent: 50 -- direction: Vertical - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Horizontal - split_size: - Percent: 50 - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 -- direction: Vertical -- direction: Vertical -- direction: Vertical -- direction: Vertical - parts: - - direction: Vertical - split_size: - Percent: 20 - run: - plugin: - location: "zellij:strider" - - direction: Horizontal - split_size: - Percent: 80 - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 -- direction: Vertical - parts: - - direction: Vertical - split_size: - Percent: 40 - - direction: Horizontal - split_size: - Percent: 60 - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 diff --git a/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml b/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml deleted file mode 100644 index aac8c4575d..0000000000 --- a/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -tabs: - - direction: Horizontal - parts: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 20 - - direction: Horizontal - split_size: - Percent: 50 - tabs: - - direction: Horizontal - split_size: - Percent: 80 - - direction: Vertical - split_size: - Percent: 20 diff --git a/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml b/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml deleted file mode 100644 index 8f3fcdbd35..0000000000 --- a/src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -tabs: - - direction: Horizontal - parts: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 20 - - direction: Horizontal - split_size: - Percent: 90 - - direction: Horizontal - tabs: - - direction: Horizontal - split_size: - Percent: 80 - - direction: Vertical - split_size: - Percent: 20 diff --git a/src/tests/fixtures/layouts/three-panes-with-nesting.yaml b/src/tests/fixtures/layouts/three-panes-with-nesting.yaml deleted file mode 100644 index 2d0aed95d8..0000000000 --- a/src/tests/fixtures/layouts/three-panes-with-nesting.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -tabs: - - direction: Horizontal - parts: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 20 - - direction: Horizontal - split_size: - Percent: 80 - split_size: - Percent: 80 - - direction: Vertical - split_size: - Percent: 20 diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index f3a8520532..f001cfbff1 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -166,7 +166,6 @@ impl std::error::Error for LayoutNameInTabError { #[cfg(test)] mod config_test { use std::io::Write; - use strum::IntoEnumIterator; use tempfile::tempdir; diff --git a/zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml b/zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml deleted file mode 100644 index 6b5993732d..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml +++ /dev/null @@ -1,41 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 21 - - direction: Vertical - split_size: - Percent: 79 - parts: - - direction: Horizontal - split_size: - Percent: 22 - - direction: Horizontal - split_size: - Percent: 78 - parts: - - direction: Horizontal - split_size: - Percent: 23 - - direction: Vertical - body: true - split_size: - Percent: 90 - - direction: Vertical - split_size: - Percent: 15 - - direction: Vertical - split_size: - Percent: 15 - - direction: Vertical - split_size: - Percent: 15 - -tabs: - - direction: Horizontal - split_size: - Percent: 24 diff --git a/zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml b/zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml deleted file mode 100644 index 6537875918..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -direction: Horizontal -parts: - - direction: Horizontal - parts: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 50 - - direction: Horizontal - tabs: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 diff --git a/zellij-utils/src/input/unit/fixtures/layouts/no-layout-template-specified.yaml b/zellij-utils/src/input/unit/fixtures/layouts/no-layout-template-specified.yaml deleted file mode 100644 index 7794c9120f..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/no-layout-template-specified.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -tabs: - - direction: Vertical - parts: - - direction: Horizontal - - direction: Horizontal diff --git a/zellij-utils/src/input/unit/fixtures/layouts/no-tab-section-specified.yaml b/zellij-utils/src/input/unit/fixtures/layouts/no-tab-section-specified.yaml deleted file mode 100644 index 8b0f3daf12..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/no-tab-section-specified.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Horizontal - body: true diff --git a/zellij-utils/src/input/unit/fixtures/layouts/session-name-to-layout.yaml b/zellij-utils/src/input/unit/fixtures/layouts/session-name-to-layout.yaml deleted file mode 100644 index 144d555b95..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/session-name-to-layout.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -session: - name: "zellij-session" diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml deleted file mode 100644 index 9ad1034d43..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - split_size: - Fixed: 1 - run: - plugin: tab-bar - - direction: Horizontal - body: true - - direction: Vertical - split_size: - Fixed: 2 - run: - plugin: status-bar - -tabs: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 50 - - direction: Horizontal - parts: - - direction: Vertical - split_size: - Percent: 50 - run: - command: {cmd: htop} - - direction: Vertical - split_size: - Percent: 50 - run: - command: {cmd: htop, args: ["-C"]} diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml deleted file mode 100644 index 8148fd2008..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml +++ /dev/null @@ -1,32 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Vertical - split_size: - Fixed: 1 - run: - plugin: - location: "zellij:tab-bar" - - direction: Horizontal - body: true - - direction: Vertical - split_size: - Fixed: 2 - run: - plugin: - location: "zellij:status-bar" -tabs: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 50 - - direction: Horizontal - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml deleted file mode 100644 index 83f05076c5..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -template: - direction: Horizontal - parts: - - direction: Horizontal - body: true - -tabs: - - direction: Vertical - parts: - - direction: Horizontal - split_size: - Percent: 50 - - direction: Horizontal - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Vertical - split_size: - Percent: 50 diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml deleted file mode 100644 index e2a09a739d..0000000000 --- a/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml +++ /dev/null @@ -1,29 +0,0 @@ ---- -template: - direction: Vertical - parts: - - direction: Horizontal - body: true - - direction: Horizontal - - -tabs: - - direction: Horizontal - split_size: - Percent: 50 - - direction: Horizontal - split_size: - Percent: 50 - parts: - - direction: Horizontal - split_size: - Percent: 50 - - direction: Horizontal - - direction: Vertical - split_size: - Percent: 50 - parts: - - direction: Vertical - split_size: - Percent: 50 - - direction: Horizontal diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index 1bc584d4e2..b9b06f31e4 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -3,9 +3,6 @@ use super::super::keybinds::*; use crate::input::config::Config; use crate::data::{self, CharOrArrow, Key}; -// TODO: make sure these are all covered -// TODO: move keybind tests here - #[test] fn can_define_keybindings_in_configfile() { let config_contents = r#" From 6aa9882cb807d0f1e9e7b026ced31407d78385ed Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 8 Sep 2022 18:14:10 +0200 Subject: [PATCH 27/55] fix(kdl): add verbose parsing errors --- zellij-server/src/lib.rs | 21 ---- zellij-server/src/panes/tiled_panes/mod.rs | 5 - zellij-utils/src/input/config.rs | 57 +-------- zellij-utils/src/input/layout.rs | 22 +--- zellij-utils/src/input/unit/keybinds_test.rs | 28 +++++ zellij-utils/src/input/unit/layout_test.rs | 117 ++++++++++++++++-- ..._error_received_on_unknown_input_mode.snap | 6 + ...r_received_on_unknown_key_instruction.snap | 6 + ...rror_on_multiple_layout_nodes_in_file.snap | 6 + ...rror_on_pane_templates_without_a_name.snap | 6 + ...error_on_tab_templates_without_a_name.snap | 6 + ...ut_test__error_on_unknown_layout_node.snap | 6 + ...error_on_unknown_layout_pane_property.snap | 6 + ...unknown_layout_pane_template_property.snap | 6 + ..._error_on_unknown_layout_tab_property.snap | 6 + ..._unknown_layout_tab_template_property.snap | 6 + zellij-utils/src/kdl/kdl_layout_parser.rs | 84 ++++++++----- zellij-utils/src/kdl/mod.rs | 28 +++-- 18 files changed, 269 insertions(+), 153 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 0d4f8589d0..a5d3b85d20 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -332,17 +332,12 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { .unwrap() }; - // if !&layout.tabs.is_empty() { if layout.has_tabs() { - log::info!("layout.has_tabs()"); - // for tab_layout in layout.clone().tabs { for (tab_name, tab_layout) in layout.tabs() { spawn_tabs(Some(tab_layout.clone()), tab_name); } - log::info!("done spawned tab"); if let Some(focused_tab_index) = layout.focused_tab_index() { - log::info!("focused_tab_index: {:?}", focused_tab_index); session_data .read() .unwrap() @@ -352,23 +347,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { .send_to_pty(PtyInstruction::GoToTab((focused_tab_index + 1) as u32, client_id)) .unwrap(); } -// let focused_tab = layout -// .tabs -// .into_iter() -// .enumerate() -// .find(|(_, tab_layout)| tab_layout.focus.unwrap_or(false)); -// if let Some((tab_index, _)) = focused_tab { -// session_data -// .read() -// .unwrap() -// .as_ref() -// .unwrap() -// .senders -// .send_to_pty(PtyInstruction::GoToTab((tab_index + 1) as u32, client_id)) -// .unwrap(); -// } } else { - log::info!("in else"); spawn_tabs(None, None); } session_data diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 74f11bdfd9..ce2336ba35 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -155,8 +155,6 @@ impl TiledPanes { removed_pane } pub fn insert_pane(&mut self, pane_id: PaneId, mut pane: Box) { - log::info!(""); - log::info!("inserting pane"); let cursor_height_width_ratio = self.cursor_height_width_ratio(); let pane_grid = TiledPaneGrid::new( &mut self.panes, @@ -171,7 +169,6 @@ impl TiledPanes { let pane_to_split = self.panes.get_mut(&pane_id_to_split).unwrap(); let size_of_both_panes = pane_to_split.position_and_size(); if let Some((first_geom, second_geom)) = split(split_direction, &size_of_both_panes) { - log::info!("first_geom: {:?}, second_geom: {:?}", first_geom, second_geom); pane_to_split.set_geom(first_geom); pane.set_geom(second_geom); self.panes.insert(pane_id, pane); @@ -223,11 +220,9 @@ impl TiledPanes { ); let result = match direction { SplitDirection::Horizontal => { - log::info!("relayout horizontal"); pane_grid.layout(direction, (*self.display_area.borrow()).cols) }, SplitDirection::Vertical => { - log::info!("relayout vertical"); pane_grid.layout(direction, (*self.display_area.borrow()).rows) } }; diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index f001cfbff1..8acb70ed5c 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -1,21 +1,13 @@ -//! Deserializes configuration options. -use std::fmt; use std::fs::File; use std::io::{self, Read}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; +use std::path::PathBuf; use thiserror::Error; -use crate::data::{self, Palette, PaletteColor, PluginTag, CharOrArrow, Key, InputMode}; +use crate::data::{Palette, PaletteColor, PluginTag, InputMode}; use super::layout::RunPluginLocation; -use super::actions::{Action, Direction}; +use std::collections::HashMap; -use std::collections::{HashMap, HashSet}; - -use kdl::{KdlDocument, KdlValue, KdlNode}; - -use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use super::keybinds::Keybinds; use super::options::{Options, OnForceClose, Clipboard}; @@ -24,9 +16,7 @@ use super::theme::{UiConfig, Theme, Themes, FrameConfig}; use crate::cli::{CliArgs, Command}; use crate::envs::EnvironmentVariables; use crate::setup; -use crate::{entry_count, kdl_entries_as_i64, kdl_first_entry_as_string, kdl_first_entry_as_i64}; -// const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml"; const DEFAULT_CONFIG_FILE_NAME: &str = "config.kdl"; type ConfigResult = Result; @@ -49,9 +39,6 @@ pub enum ConfigError { KdlDeserializationError(#[from] kdl::KdlError), #[error("KdlDeserialization error: {0}")] KdlParsingError(String), - // Deserialization error - #[error("Deserialization error: {0}")] - Serde(#[from] serde_yaml::Error), // Io error #[error("IoError: {0}")] Io(#[from] io::Error), @@ -63,9 +50,6 @@ pub enum ConfigError { // Internal Deserialization Error #[error("FromUtf8Error: {0}")] FromUtf8(#[from] std::string::FromUtf8Error), - // Naming a part in a tab is unsupported - #[error("There was an error in the layout file, {0}")] - LayoutNameInTab(#[from] LayoutNameInTabError), // Plugins have a semantic error, usually trying to parse two of the same tag #[error("PluginsError: {0}")] PluginsError(#[from] PluginsConfigError), @@ -78,7 +62,6 @@ impl TryFrom<&CliArgs> for Config { if let Some(ref path) = opts.config { let default_config = Config::from_default_assets()?; return Config::from_path(path, Some(default_config)); - // return Config::new(path); } if let Some(Command::Setup(ref setup)) = opts.command { @@ -131,38 +114,6 @@ impl Config { } } -// TODO: Split errors up into separate modules -#[derive(Debug, Clone)] -pub struct LayoutNameInTabError; - -impl fmt::Display for LayoutNameInTabError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "LayoutNameInTabError: -The `parts` inside the `tabs` can't be named. For example: ---- -tabs: - - direction: Vertical - name: main - parts: - - direction: Vertical - name: section # <== The part section can't be named. - - direction: Vertical - - direction: Vertical - name: test -" - ) - } -} - -impl std::error::Error for LayoutNameInTabError { - fn description(&self) -> &str { - "The `parts` inside the `tabs` can't be named." - } -} - -// The unit test location. #[cfg(test)] mod config_test { use std::io::Write; diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index db02aaab00..78df1b8b0a 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -11,7 +11,7 @@ use crate::{ input::{ command::RunCommand, - config::{Config, ConfigError, LayoutNameInTabError}, + config::{Config, ConfigError}, }, pane_size::{Dimension, PaneGeom}, setup, @@ -110,26 +110,6 @@ impl fmt::Display for RunPluginLocation { } } -// // The layout struct ultimately used to build the layouts. -// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] -// pub struct Layout { -// pub direction: SplitDirection, -// #[serde(default)] -// pub pane_name: Option, -// #[serde(default)] -// pub parts: LayoutParts, -// pub split_size: Option, -// pub run: Option, -// #[serde(default)] -// pub borderless: bool, -// pub focus: Option, -// pub external_children_index: Option, -// pub focused_tab_index: Option, -// pub template: Option>, -// } - -// TODO: CONTINUE HERE - keep following the compiler in kdl_layout_parser.rs -// to work with these new refactored structs #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] pub struct Layout { pub tabs: Vec<(Option, PaneLayout)>, diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index b9b06f31e4..2d4ed39447 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -2,6 +2,7 @@ use super::super::actions::*; use super::super::keybinds::*; use crate::input::config::Config; use crate::data::{self, CharOrArrow, Key}; +use insta::assert_snapshot; #[test] fn can_define_keybindings_in_configfile() { @@ -460,3 +461,30 @@ fn keybindings_unbinds_happen_after_binds() { ); assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound"); } + +#[test] +fn error_received_on_unknown_input_mode() { + let config_contents = r#" + keybinds { + i_do_not_exist { + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let config_error = Config::from_kdl(config_contents, None).unwrap_err(); + assert_snapshot!(format!("{:?}", config_error)); +} + +#[test] +fn error_received_on_unknown_key_instruction() { + let config_contents = r#" + keybinds { + pane { + i_am_not_bind_or_unbind + bind "z" { SwitchToMode "Resize"; } + } + } + "#; + let config_error = Config::from_kdl(config_contents, None).unwrap_err(); + assert_snapshot!(format!("{:?}", config_error)); +} diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index ea0529c8f4..fd66036a9d 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -3,18 +3,6 @@ use super::super::layout::*; use std::convert::TryInto; use insta::assert_snapshot; -fn layout_test_dir(layout: String) -> PathBuf { - let root = Path::new(env!("CARGO_MANIFEST_DIR")); - let layout_dir = root.join("src/input/unit/fixtures/layouts"); - layout_dir.join(layout) -} - -fn default_layout_dir(layout: String) -> PathBuf { - let root = Path::new(env!("CARGO_MANIFEST_DIR")); - let layout_dir = root.join("assets/layouts"); - layout_dir.join(layout) -} - #[test] fn empty_layout() { let kdl_layout = "layout"; @@ -946,3 +934,108 @@ fn cannot_define_pane_template_names_as_keywords() { assert!(layout.is_err(), "{}", format!("error provided for pane template name with keyword: {}", keyword)); } } + +#[test] +fn error_on_multiple_layout_nodes_in_file() { + let kdl_layout = format!(" + layout + layout + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_unknown_layout_node() { + let kdl_layout = format!(" + layout {{ + pane + i_am_not_a_proper_node + pane + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_unknown_layout_pane_property() { + let kdl_layout = format!(" + layout {{ + pane spit_size=1 + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_unknown_layout_pane_template_property() { + let kdl_layout = format!(" + layout {{ + pane_template name=\"my_cool_template\" spit_size=1 + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_unknown_layout_tab_property() { + let kdl_layout = format!(" + layout {{ + tab spit_size=1 + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_unknown_layout_tab_template_property() { + let kdl_layout = format!(" + layout {{ + tab_template name=\"my_cool_template\" spit_size=1 + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_pane_templates_without_a_name() { + let kdl_layout = format!(" + layout {{ + pane_template {{ + pane + children + pane + }} + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} + +#[test] +fn error_on_tab_templates_without_a_name() { + let kdl_layout = format!(" + layout {{ + tab_template {{ + pane + children + pane + }} + }} + "); + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap new file mode 100644 index 0000000000..8ced6a81bd --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/keybinds_test.rs +assertion_line: 475 +expression: "format!(\"{:?}\", config_error)" +--- +Std("unknown mode \"i_do_not_exist\"") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap new file mode 100644 index 0000000000..6d7a81048a --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/keybinds_test.rs +assertion_line: 489 +expression: "format!(\"{:?}\", config_error)" +--- +KdlParsingError("Unknown keybind instruction: 'i_am_not_bind_or_unbind'") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap new file mode 100644 index 0000000000..508020f1ce --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 946 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Only one layout node per file allowed") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap new file mode 100644 index 0000000000..8e63fd7f05 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1000 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Pane templates must have a name") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap new file mode 100644 index 0000000000..dc2c3f1c3d --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1016 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Tab templates must have a name") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap new file mode 100644 index 0000000000..af09d556ee --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 960 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Unknown layout node: 'i_am_not_a_proper_node'") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap new file mode 100644 index 0000000000..5df1eebf24 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 972 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Invalid pane property 'spit_size'") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap new file mode 100644 index 0000000000..01b8c275c8 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 984 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Invalid pane property 'spit_size'") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap new file mode 100644 index 0000000000..9603a98398 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 996 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Invalid tab property 'spit_size'") diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap new file mode 100644 index 0000000000..b638b297c7 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1008 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Invalid tab property 'spit_size'") diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 7cd543cfda..14165087cd 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -1,22 +1,7 @@ -//! The layout system. -// Layouts have been moved from [`zellij-server`] to -// [`zellij-utils`] in order to provide more helpful -// error messages to the user until a more general -// logging system is in place. -// In case there is a logging system in place evaluate, -// if [`zellij-utils`], or [`zellij-server`] is a proper -// place. -// If plugins should be able to depend on the layout system -// then [`zellij-utils`] could be a proper place. -use crate::{ - input::{ - command::RunCommand, - config::{ConfigError, LayoutNameInTabError}, - layout::{Layout, PaneLayout, LayoutParts, SplitDirection, Run, RunPlugin, RunPluginLocation, SplitSize}, - plugins::{PluginTag, PluginsConfigError}, - }, - pane_size::{Dimension, PaneGeom}, - setup, +use crate::input::{ + command::RunCommand, + config::ConfigError, + layout::{Layout, PaneLayout, SplitDirection, Run, RunPlugin, RunPluginLocation, SplitSize}, }; use kdl::*; @@ -25,31 +10,20 @@ use std::str::FromStr; use std::collections::{HashMap, HashSet}; use crate::{ - kdl_children, kdl_string_arguments, kdl_children_nodes, kdl_name, - kdl_document_name, kdl_get_string_entry, - kdl_get_int_entry, - kdl_get_child_entry_bool_value, - kdl_get_child_entry_string_value, kdl_get_child, kdl_get_bool_property_or_child_value, kdl_get_string_property_or_child_value, kdl_get_int_property_or_child_value, + kdl_property_names, }; -use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::vec::Vec; -use std::{ - cmp::max, - fmt, fs, - ops::Not, - path::{Path, PathBuf}, -}; -use std::{fs::File, io::prelude::*}; +use std::path::PathBuf; use url::Url; pub struct KdlLayoutParser <'a>{ @@ -79,6 +53,22 @@ impl <'a>KdlLayoutParser <'a> { word == "children" || word == "tab" } + fn is_a_valid_pane_property(&self, property_name: &str) -> bool { + property_name == "borderless" || + property_name == "focus" || + property_name == "name" || + property_name == "size" || + property_name == "plugin" || + property_name == "command" || + property_name == "cwd" || + property_name == "args" || + property_name == "split_direction" + } + fn is_a_valid_tab_property(&self, property_name: &str) -> bool { + property_name == "focus" || + property_name == "name" || + property_name == "split_direction" + } fn assert_legal_node_name(&self, name: &str) -> Result<(), ConfigError> { if name.contains(char::is_whitespace) { Err(ConfigError::KdlParsingError(format!("Node names ({}) cannot contain whitespace.", name))) @@ -142,6 +132,7 @@ impl <'a>KdlLayoutParser <'a> { Ok(run) } fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { + self.assert_valid_pane_properties(kdl_node)?; let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); let name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|name| name.to_string()); @@ -193,6 +184,7 @@ impl <'a>KdlLayoutParser <'a> { }) } fn parse_pane_template_node(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name + self.assert_valid_pane_properties(kdl_node)?; let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()).ok_or(ConfigError::KdlParsingError("Pane templates must have a name".into()))?; self.assert_legal_node_name(&template_name)?; let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); @@ -217,6 +209,7 @@ impl <'a>KdlLayoutParser <'a> { Ok(()) } fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(Option, PaneLayout), ConfigError> { // String is the tab name + self.assert_valid_tab_properties(kdl_node)?; match self.default_tab_template.as_ref().map(|t| t.clone()) { Some(default_tab_template) => { self.parse_tab_node_with_template(kdl_node, default_tab_template) @@ -274,6 +267,24 @@ impl <'a>KdlLayoutParser <'a> { } Ok(()) } + fn assert_valid_pane_properties(&self, pane_node: &KdlNode) -> Result<(), ConfigError> { + let all_property_names = kdl_property_names!(pane_node); + for name in all_property_names { + if !self.is_a_valid_pane_property(name) { + return Err(ConfigError::KdlParsingError(format!("Invalid pane property '{}'", name))); + } + } + Ok(()) + } + fn assert_valid_tab_properties(&self, pane_node: &KdlNode) -> Result<(), ConfigError> { + let all_property_names = kdl_property_names!(pane_node); + for name in all_property_names { + if !self.is_a_valid_tab_property(name) { + return Err(ConfigError::KdlParsingError(format!("Invalid tab property '{}'", name))); + } + } + Ok(()) + } fn insert_layout_children_or_error(&self, layout: &mut PaneLayout, mut child_panes_layout: PaneLayout) -> Result<(), ConfigError> { let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?; if !successfully_inserted { @@ -319,6 +330,7 @@ impl <'a>KdlLayoutParser <'a> { Ok(()) } fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result { + self.assert_valid_tab_properties(kdl_node)?; let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), @@ -494,11 +506,17 @@ impl <'a>KdlLayoutParser <'a> { return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); } child_panes.push(self.parse_pane_node_with_template(child, pane_template)?); + } else if !self.is_a_reserved_word(child_name) { + return Err(ConfigError::KdlParsingError(format!("Unknown layout node: '{}'", child_name))); } Ok(()) } pub fn parse(&mut self) -> Result { let layout_node = self.kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; + let has_multiple_layout_nodes = self.kdl_layout.nodes().iter().filter(|n| kdl_name!(n) == "layout").count() > 1; + if has_multiple_layout_nodes { + return Err(ConfigError::KdlParsingError("Only one layout node per file allowed".into())); + } let mut child_tabs = vec![]; let mut child_panes = vec![]; if let Some(children) = kdl_children_nodes!(layout_node) { diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 9853af3db6..5af1aa22b1 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,7 +1,6 @@ mod kdl_layout_parser; use kdl_layout_parser::KdlLayoutParser; use strum::IntoEnumIterator; -use strum_macros::EnumIter; use crate::envs::EnvironmentVariables; use crate::input::command::RunCommand; use crate::input::keybinds::Keybinds; @@ -13,10 +12,8 @@ use crate::input::options::{Options, OnForceClose, Clipboard}; use std::collections::HashMap; use std::fs::File; use std::io::Read; -use crate::input::plugins::{PluginsConfig, PluginsConfigError, PluginConfig, PluginType, PluginTag}; +use crate::input::plugins::{PluginsConfig, PluginConfig, PluginType, PluginTag}; use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; -use crate::cli::{CliArgs, Command}; -use crate::setup; use kdl::{KdlDocument, KdlValue, KdlNode}; @@ -172,6 +169,13 @@ macro_rules! kdl_string_arguments { }} } +#[macro_export] +macro_rules! kdl_property_names { + ( $kdl_node:expr ) => {{ + $kdl_node.entries().iter().filter_map(|e| e.name()).map(|e| e.value()) + }} +} + #[macro_export] macro_rules! kdl_argument_values { ( $kdl_node:expr ) => { @@ -773,15 +777,23 @@ impl EnvironmentVariables { impl Keybinds { fn bind_keys_in_block(block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError> { - let bind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); - let unbind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); + let all_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode"); +// let bind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); +// let unbind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); + let bind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "bind"); + let unbind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "unbind"); for key_block in bind_nodes { Keybinds::bind_actions_for_each_key(key_block, input_mode_keybinds)?; } - // we loop twice so that the unbinds always happen after the binds + // we loop a second time so that the unbinds always happen after the binds for key_block in unbind_nodes { Keybinds::unbind_keys(key_block, input_mode_keybinds)?; } + for key_block in all_nodes { + if kdl_name!(key_block) != "bind" && kdl_name!(key_block) != "unbind" { + return Err(ConfigError::KdlParsingError(format!("Unknown keybind instruction: '{}'", kdl_name!(key_block)))); + } + } Ok(()) } pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result { @@ -980,7 +992,7 @@ impl Theme { // String is the theme name let mut file = File::open(path_to_theme_file)?; let mut kdl_config = String::new(); - file.read_to_string(&mut kdl_config); + file.read_to_string(&mut kdl_config)?; let kdl_config: KdlDocument = kdl_config.parse()?; let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::KdlParsingError("No theme found in file".into()))?; let theme_name = kdl_name!(kdl_config); From 1aaefa0d549e318325a07bd83a6468441c941354 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 9 Sep 2022 15:15:21 +0200 Subject: [PATCH 28/55] fix(kdl): focused tab --- zellij-utils/src/input/unit/layout_test.rs | 38 +++++++++++++++++++ ...t__error_on_more_than_one_focused_tab.snap | 6 +++ zellij-utils/src/kdl/kdl_layout_parser.rs | 23 +++++++---- 3 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index fd66036a9d..be9a2bcf73 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -430,6 +430,30 @@ fn layout_with_tab_names() { assert_eq!(layout, expected_layout); } +#[test] +fn layout_with_focused_tab() { + let kdl_layout = r#" + layout { + tab + tab focus=true + tab + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let expected_layout = Layout { + tabs: vec![ + (None, PaneLayout::default()), + (None, PaneLayout::default()), + (None, PaneLayout::default()), + ], + template: Some(PaneLayout::default()), + focused_tab_index: Some(1), + ..Default::default() + }; + assert_eq!(layout, expected_layout); +} + #[test] fn layout_with_tab_templates() { let kdl_layout = r#" @@ -1039,3 +1063,17 @@ fn error_on_tab_templates_without_a_name() { let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } + +#[test] +fn error_on_more_than_one_focused_tab() { + let kdl_layout = r#" + layout { + tab focus=true + tab focus=true + tab + } + "#; + let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); + let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + assert_snapshot!(format!("{:?}", layout_error)); +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap new file mode 100644 index 0000000000..53e1546f68 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap @@ -0,0 +1,6 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1078 +expression: "format!(\"{:?}\", layout_error)" +--- +KdlParsingError("Only one tab can be focused") diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 14165087cd..92254e7c73 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -208,7 +208,7 @@ impl <'a>KdlLayoutParser <'a> { }); Ok(()) } - fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(Option, PaneLayout), ConfigError> { // String is the tab name + fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(bool, Option, PaneLayout), ConfigError> { // (is_focused, Option, PaneLayout) self.assert_valid_tab_properties(kdl_node)?; match self.default_tab_template.as_ref().map(|t| t.clone()) { Some(default_tab_template) => { @@ -216,6 +216,7 @@ impl <'a>KdlLayoutParser <'a> { }, None => { let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), @@ -224,7 +225,7 @@ impl <'a>KdlLayoutParser <'a> { Some(children) => self.parse_child_pane_nodes_for_tab(children)?, None => vec![], }; - Ok((tab_name, PaneLayout { + Ok((is_focused, tab_name, PaneLayout { children_split_direction, children, ..Default::default() @@ -293,8 +294,9 @@ impl <'a>KdlLayoutParser <'a> { Ok(()) } } - fn parse_tab_node_with_template(&mut self, kdl_node: &KdlNode, mut tab_layout: PaneLayout) -> Result<(Option, PaneLayout), ConfigError> { // String is the tab name + fn parse_tab_node_with_template(&mut self, kdl_node: &KdlNode, mut tab_layout: PaneLayout) -> Result<(bool, Option, PaneLayout), ConfigError> { // (is_focused, Option, PaneLayout) let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), @@ -317,7 +319,7 @@ impl <'a>KdlLayoutParser <'a> { } } tab_layout.external_children_index = None; - Ok((tab_name, tab_layout)) + Ok((is_focused, tab_name, tab_layout)) } fn populate_one_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()).ok_or(ConfigError::KdlParsingError("Tab templates must have a name".into()))?; @@ -447,12 +449,13 @@ impl <'a>KdlLayoutParser <'a> { } Ok(()) } - fn layout_with_tabs(&self, tabs: Vec<(Option, PaneLayout)>) -> Result { + fn layout_with_tabs(&self, tabs: Vec<(Option, PaneLayout)>, focused_tab_index: Option) -> Result { let template = self.default_template()?.unwrap_or_else(|| PaneLayout::default()); Ok(Layout { tabs: tabs, template: Some(template), + focused_tab_index, ..Default::default() }) } @@ -484,7 +487,7 @@ impl <'a>KdlLayoutParser <'a> { ..Default::default() }) } - fn populate_layout_child(&mut self, child: &KdlNode, child_tabs: &mut Vec<(Option, PaneLayout)>, child_panes: &mut Vec) -> Result<(), ConfigError> { + fn populate_layout_child(&mut self, child: &KdlNode, child_tabs: &mut Vec<(bool, Option, PaneLayout)>, child_panes: &mut Vec) -> Result<(), ConfigError> { let child_name = kdl_name!(child); if child_name == "pane" { if !child_tabs.is_empty() { @@ -527,7 +530,13 @@ impl <'a>KdlLayoutParser <'a> { } } if !child_tabs.is_empty() { - self.layout_with_tabs(child_tabs) + let has_more_than_one_focused_tab = child_tabs.iter().filter(|(is_focused, _, _)| *is_focused).count() > 1; + if has_more_than_one_focused_tab { + return Err(ConfigError::KdlParsingError("Only one tab can be focused".into())); + } + let focused_tab_index = child_tabs.iter().position(|(is_focused, _, _)| *is_focused); + let child_tabs: Vec<(Option, PaneLayout)> = child_tabs.drain(..).map(|(_is_focused, tab_name, pane_layout)| (tab_name, pane_layout)).collect(); + self.layout_with_tabs(child_tabs, focused_tab_index) } else if !child_panes.is_empty() { self.layout_with_one_tab(child_panes) } else { From 0e1e774160a6b81f42c336d5e4b623d83e4b0ea4 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 9 Sep 2022 15:44:47 +0200 Subject: [PATCH 29/55] fix(layout): corret default_tab_template behavior --- Cargo.lock | 1 - zellij-utils/Cargo.toml | 1 - zellij-utils/src/input/unit/layout_test.rs | 59 +------- ...est__layout_with_default_tab_template.snap | 138 ++++++++++++++++++ zellij-utils/src/kdl/kdl_layout_parser.rs | 37 ++--- zellij-utils/src/lib.rs | 2 +- 6 files changed, 156 insertions(+), 82 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap diff --git a/Cargo.lock b/Cargo.lock index ee4a7ced27..ff3ee157d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3491,7 +3491,6 @@ dependencies = [ "rmp-serde", "serde", "serde_json", - "serde_yaml", "signal-hook 0.3.14", "strip-ansi-escapes", "strum", diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 419f6dd37f..62831c160e 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -23,7 +23,6 @@ libc = "0.2" nix = "0.23.1" once_cell = "1.8.0" serde = { version = "1.0", features = ["derive"] } -serde_yaml = "0.8" serde_json = "1.0" strip-ansi-escapes = "0.1.0" strum = "0.20.0" diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index be9a2bcf73..2954030ac5 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -532,7 +532,7 @@ fn layout_with_default_tab_template() { layout { default_tab_template { pane - children + pane pane } tab name="my first tab" split_direction="Vertical" { @@ -548,62 +548,7 @@ fn layout_with_default_tab_template() { "#; let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); let layout = Layout::from_kdl(&kdl_layout).unwrap(); - let expected_layout = Layout { - tabs: vec![ - (Some("my first tab".into()), PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout { - children_split_direction: SplitDirection::Vertical, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }, - PaneLayout::default(), - ], - ..Default::default() - }), - (Some("my second tab".into()), PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }, - PaneLayout::default(), - ], - ..Default::default() - }), - (None, PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }), - ], - template: Some(PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }), - ..Default::default() - }; - assert_eq!(layout, expected_layout); + assert_snapshot!(format!("{:#?}", layout)); } #[test] diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap new file mode 100644 index 0000000000..92dfd4418d --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap @@ -0,0 +1,138 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 551 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + Some( + "my first tab", + ), + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ( + Some( + "my second tab", + ), + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 92254e7c73..69b0a2d491 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -210,28 +210,21 @@ impl <'a>KdlLayoutParser <'a> { } fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(bool, Option, PaneLayout), ConfigError> { // (is_focused, Option, PaneLayout) self.assert_valid_tab_properties(kdl_node)?; - match self.default_tab_template.as_ref().map(|t| t.clone()) { - Some(default_tab_template) => { - self.parse_tab_node_with_template(kdl_node, default_tab_template) - }, - None => { - let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); - let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); - let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { - Some(direction) => SplitDirection::from_str(direction)?, - None => SplitDirection::default(), - }; - let children = match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_tab(children)?, - None => vec![], - }; - Ok((is_focused, tab_name, PaneLayout { - children_split_direction, - children, - ..Default::default() - })) - } - } + let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); + let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + Some(direction) => SplitDirection::from_str(direction)?, + None => SplitDirection::default(), + }; + let children = match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_tab(children)?, + None => vec![], + }; + Ok((is_focused, tab_name, PaneLayout { + children_split_direction, + children, + ..Default::default() + })) } fn parse_child_pane_nodes_for_tab(&self, children: &[KdlNode]) -> Result, ConfigError> { let mut nodes = vec![]; diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs index c8beae7b22..47cf94be17 100644 --- a/zellij-utils/src/lib.rs +++ b/zellij-utils/src/lib.rs @@ -21,6 +21,6 @@ pub mod logging; // Requires log4rs #[cfg(not(target_family = "wasm"))] pub use ::{ - anyhow, async_std, clap, interprocess, lazy_static, libc, nix, regex, serde, serde_yaml, + anyhow, async_std, clap, interprocess, lazy_static, libc, nix, regex, serde, signal_hook, tempfile, termwiz, vte, }; From 3a13ea927e337edc0595e97ead6c59ca6b6c703b Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 9 Sep 2022 17:03:46 +0200 Subject: [PATCH 30/55] style(code): fix compile warnings --- zellij-client/src/input_handler.rs | 1 - zellij-server/src/screen.rs | 4 +- zellij-server/src/tab/mod.rs | 2 +- zellij-server/src/unit/screen_tests.rs | 3 +- zellij-utils/src/envs.rs | 3 - zellij-utils/src/input/actions.rs | 2 +- zellij-utils/src/input/command.rs | 4 - zellij-utils/src/input/config.rs | 19 +- zellij-utils/src/input/keybinds.rs | 5 - zellij-utils/src/input/layout.rs | 188 +----------------- zellij-utils/src/input/mod.rs | 1 - zellij-utils/src/input/options.rs | 1 - zellij-utils/src/input/plugins.rs | 7 - zellij-utils/src/input/theme.rs | 2 +- zellij-utils/src/input/unit/keybinds_test.rs | 1 + zellij-utils/src/input/unit/layout_test.rs | 2 - ...__theme_test__dracula_theme_from_file.snap | 109 ++++++++++ zellij-utils/src/setup.rs | 2 +- 18 files changed, 134 insertions(+), 222 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs index 223b3fbcc1..ecaeeedc1d 100644 --- a/zellij-client/src/input_handler.rs +++ b/zellij-client/src/input_handler.rs @@ -11,7 +11,6 @@ use zellij_utils::{ actions::Action, cast_termwiz_key, config::Config, - keybinds::Keybinds, mouse::{MouseButton, MouseEvent}, options::Options, }, diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index f6df262595..5d64186a22 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -8,7 +8,7 @@ use std::str; use zellij_utils::input::options::Clipboard; use zellij_utils::pane_size::{Size, SizeInPixels}; -use zellij_utils::{input::command::TerminalAction, input::layout::{PaneLayout, Layout}, position::Position}; +use zellij_utils::{input::command::TerminalAction, input::layout::PaneLayout, position::Position}; use crate::panes::alacritty_functions::xparse_color; use crate::panes::terminal_character::AnsiCode; @@ -1430,6 +1430,6 @@ pub(crate) fn screen_thread_main( } } -#[cfg(test)] #[path = "./unit/screen_tests.rs"] +#[cfg(test)] mod screen_tests; diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 40684c44a9..6a1f27a937 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -40,7 +40,7 @@ use zellij_utils::{ data::{Event, InputMode, ModeInfo, Palette, PaletteColor, Style}, input::{ command::TerminalAction, - layout::{PaneLayout, Layout, Run}, + layout::{PaneLayout, Run}, parse_keys, }, pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 51cc616372..15526ac8e2 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -5,10 +5,9 @@ use crate::{ thread_bus::Bus, ClientId, }; -use std::convert::TryInto; use std::path::PathBuf; use zellij_utils::input::command::TerminalAction; -use zellij_utils::input::layout::{PaneLayout, Layout}; +use zellij_utils::input::layout::PaneLayout; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index e0df159b68..1cd203efe0 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -1,9 +1,6 @@ /// Uniformly operates ZELLIJ* environment variables use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::input::config::ConfigError; -use kdl::KdlNode; -use crate::{kdl_children_nodes_or_error, kdl_name, kdl_first_entry_as_string, kdl_first_entry_as_i64}; use std::{ collections::{HashMap, BTreeMap}, env::{set_var, var}, diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 5cca66f9a6..9b210dd9c4 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,7 +1,7 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::{PaneLayout, Layout}; +use super::layout::PaneLayout; use crate::data::InputMode; use crate::input::options::OnForceClose; use serde::{Deserialize, Serialize}; diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs index e8df9637ac..6f093a4a8b 100644 --- a/zellij-utils/src/input/command.rs +++ b/zellij-utils/src/input/command.rs @@ -3,10 +3,6 @@ use super::actions::Direction; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use kdl::*; -use crate::{kdl_get_child, kdl_get_child_entry_string_value, kdl_string_arguments}; -use crate::input::config::ConfigError; - #[derive(Debug, Clone)] pub enum TerminalAction { OpenFile(PathBuf, Option), // path to file and optional line_number diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 8acb70ed5c..8b00f253e7 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -2,17 +2,14 @@ use std::fs::File; use std::io::{self, Read}; use std::path::PathBuf; use thiserror::Error; -use crate::data::{Palette, PaletteColor, PluginTag, InputMode}; -use super::layout::RunPluginLocation; - -use std::collections::HashMap; +use crate::data::Palette; use std::convert::TryFrom; use super::keybinds::Keybinds; -use super::options::{Options, OnForceClose, Clipboard}; -use super::plugins::{PluginsConfig, PluginsConfigError, PluginConfig, PluginType}; -use super::theme::{UiConfig, Theme, Themes, FrameConfig}; +use super::options::Options; +use super::plugins::{PluginsConfig, PluginsConfigError}; +use super::theme::{UiConfig, Themes}; use crate::cli::{CliArgs, Command}; use crate::envs::EnvironmentVariables; use crate::setup; @@ -117,9 +114,13 @@ impl Config { #[cfg(test)] mod config_test { use std::io::Write; - + use std::collections::HashMap; + use crate::data::{Palette, PaletteColor, PluginTag, InputMode}; use tempfile::tempdir; - + use crate::input::layout::RunPluginLocation; + use crate::input::options::{OnForceClose, Clipboard}; + use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; + use crate::input::plugins::{PluginsConfig, PluginConfig, PluginType}; use super::*; #[test] diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index efaa0102e3..b98a8e6342 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -1,14 +1,9 @@ -//! Mapping of inputs to sequences of actions. -use std::str::FromStr; -use kdl::{KdlDocument, KdlValue, KdlNode}; use std::collections::{BTreeMap, HashMap}; use super::actions::Action; -use super::config:: {self, ConfigError}; use crate::data::{InputMode, Key, KeybindsVec}; use serde::{Deserialize, Serialize}; -use strum::IntoEnumIterator; use std::fmt; /// Used in the config struct diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 78df1b8b0a..67087b70c2 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -20,17 +20,15 @@ use crate::{ use kdl::*; use std::str::FromStr; -use std::collections::{HashMap, HashSet}; use super::{ plugins::{PluginTag, PluginsConfigError}, }; use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::vec::Vec; use std::{ - cmp::max, - fmt, fs, + fmt, ops::Not, path::{Path, PathBuf}, }; @@ -306,91 +304,6 @@ impl Layout { Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?) } -// pub fn total_terminal_panes(&self) -> usize { -// // TODO: better -// let mut total_panes = 0; -// match &self.parts { -// LayoutParts::Panes(parts) => { -// total_panes += parts.len(); -// for part in parts { -// match part.run { -// Some(Run::Command(_)) | None => { -// total_panes += part.total_terminal_panes(); -// }, -// Some(Run::Plugin(_)) => {}, -// } -// } -// total_panes -// }, -// LayoutParts::Tabs(tabs) => { -// // let parts = tabs.values(); -// total_panes += tabs.len(); -// for tab in tabs { -// let (_tab_name, part) = tab; -// match part.run { -// Some(Run::Command(_)) | None => { -// total_panes += part.total_terminal_panes(); -// }, -// Some(Run::Plugin(_)) => {}, -// } -// } -// total_panes -// } -// } -// } - -// pub fn total_borderless_panes(&self) -> usize { -// // TODO: better -// let mut total_borderless_panes = 0; -// match &self.parts { -// LayoutParts::Panes(parts) => { -// total_borderless_panes += parts.iter().filter(|p| p.borderless).count(); -// for part in parts { -// total_borderless_panes += part.total_borderless_panes(); -// } -// total_borderless_panes -// }, -// LayoutParts::Tabs(tabs) => { -// total_borderless_panes += tabs.iter().filter(|(_, p)| p.borderless).count(); -// for part in tabs { -// let (_part_name, part) = part; -// total_borderless_panes += part.total_borderless_panes(); -// } -// total_borderless_panes -// } -// } -// } -// pub fn extract_run_instructions(&self) -> Vec> { -// // TODO: better -// let mut run_instructions = vec![]; -// match &self.parts { -// LayoutParts::Panes(parts) => { -// if parts.is_empty() { -// run_instructions.push(self.run.clone()); -// } -// for part in parts { -// let mut current_runnables = part.extract_run_instructions(); -// run_instructions.append(&mut current_runnables); -// } -// }, -// LayoutParts::Tabs(tabs) => { -// if tabs.len() == 0 { -// run_instructions.push(self.run.clone()); -// } -// for tab in tabs { -// let (_part_name, part) = tab; -// let mut current_runnables = part.extract_run_instructions(); -// run_instructions.append(&mut current_runnables); -// } -// } -// } -// run_instructions -// } - -// pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { -// split_space(space, self) -// } - pub fn new_tab(&self) -> PaneLayout { match &self.template { Some(template) => template.clone(), @@ -400,10 +313,6 @@ impl Layout { pub fn is_empty(&self) -> bool { !self.tabs.is_empty() -// match &self.parts { -// LayoutParts::Tabs(tabs) => tabs.is_empty(), -// LayoutParts::Panes(panes) => panes.is_empty(), -// } } // TODO: do we need both of these? pub fn has_tabs(&self) -> bool { @@ -412,112 +321,29 @@ impl Layout { pub fn tabs(&self) -> Vec<(Option, PaneLayout)> { // String is the tab name self.tabs.clone() -// match &self.parts { -// LayoutParts::Tabs(tabs) => tabs.clone(), -// _ => vec![] -// } } pub fn focused_tab_index(&self) -> Option { self.focused_tab_index } -// pub fn children_block_count(&self) -> usize { -// let mut count = 0; -// if self.external_children_index.is_some() { -// count += 1; -// } -// match &self.parts { -// LayoutParts::Tabs(tabs) => { -// for tab in tabs { -// count += tab.1.children_block_count(); -// } -// } -// LayoutParts::Panes(panes) => { -// for pane in panes { -// count += pane.children_block_count(); -// } -// } -// } -// count -// } -// pub fn insert_children_layout(&mut self, children_layout: &mut Layout) -> Result { -// // returns true if successfully inserted and false otherwise -// let external_children_index = self.external_children_index; -// match &mut self.parts { -// LayoutParts::Tabs(tabs) => Err(ConfigError::KdlParsingError("Cannot insert child layout in tabs".into())), -// LayoutParts::Panes(panes) => { -// match external_children_index { -// Some(external_children_index) => { -// panes.insert(external_children_index, children_layout.clone()); -// self.external_children_index = None; -// Ok(true) -// }, -// None => { -// for pane in panes.iter_mut() { -// if pane.insert_children_layout(children_layout)? { -// return Ok(true); -// } -// } -// Ok(false) -// } -// } -// } -// } -// } } -// fn layout_size(direction: SplitDirection, layout: &Layout) -> usize { -// fn child_layout_size( -// direction: SplitDirection, -// parent_direction: SplitDirection, -// layout: &Layout, -// ) -> usize { -// let size = if parent_direction == direction { 1 } else { 0 }; -// let parts_is_empty = match &layout.parts { -// LayoutParts::Panes(parts) => parts.is_empty(), -// LayoutParts::Tabs(tabs) => tabs.len() == 0 -// }; -// // if layout.parts.is_empty() { -// if parts_is_empty { -// size -// } else { -// match &layout.parts { -// LayoutParts::Panes(parts) => { -// let children_size = parts -// .iter() -// .map(|p| child_layout_size(direction, layout.direction, p)) -// .sum(); -// max(size, children_size) -// }, -// LayoutParts::Tabs(tabs) => { -// let children_size = tabs -// .iter() -// .map(|(_, p)| child_layout_size(direction, layout.direction, p)) -// .sum(); -// max(size, children_size) -// } -// } -// } -// } -// child_layout_size(direction, direction, layout) -// } - fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_split: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { let mut pane_positions = Vec::new(); let sizes: Vec> = layout.children.iter().map(|part| part.split_size).collect(); let mut split_geom = Vec::new(); - let (mut current_position, split_dimension_space, mut inherited_dimension, total_split_dimension_space, total_inherited_dimension_space) = + let (mut current_position, split_dimension_space, inherited_dimension, total_split_dimension_space) = match layout.children_split_direction { - SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows, total_space_to_split.cols, total_space_to_split.rows), - SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols, total_space_to_split.rows, total_space_to_split.cols), + SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows, total_space_to_split.cols), + SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols, total_space_to_split.rows), }; let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); let mut total_pane_size = 0; - for (&size, part) in sizes.iter().zip(&*layout.children) { + for (&size, _part) in sizes.iter().zip(&*layout.children) { let mut split_dimension = match size { Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), Some(SplitSize::Fixed(size)) => Dimension::fixed(size), @@ -631,6 +457,6 @@ impl FromStr for SplitSize { } // The unit test location. -#[cfg(test)] #[path = "./unit/layout_test.rs"] +#[cfg(test)] mod layout_test; diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs index 29a7816cd6..6722cfd886 100644 --- a/zellij-utils/src/input/mod.rs +++ b/zellij-utils/src/input/mod.rs @@ -1,4 +1,3 @@ -//! The way terminal input is handled. pub mod actions; pub mod command; pub mod config; diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 92750aed65..e722cb5b9b 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -5,7 +5,6 @@ use clap::{ArgEnum, Args}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::str::FromStr; -use kdl::{KdlDocument, KdlValue, KdlNode}; #[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ArgEnum)] pub enum OnForceClose { diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index 5d659eb28a..60310752ba 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -1,22 +1,15 @@ //! Plugins configuration metadata use std::borrow::Borrow; use std::collections::HashMap; -use std::convert::TryFrom; use std::fs; use std::path::{Path, PathBuf}; use thiserror::Error; -use kdl::KdlNode; -use crate::{kdl_children_nodes_or_error, kdl_name, kdl_property_first_arg_as_string, kdl_children_property_first_arg_as_string, kdl_children_property_first_arg_as_bool}; - -use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use url::Url; -use super::config::ConfigError; use super::layout::{RunPlugin, RunPluginLocation}; pub use crate::data::PluginTag; -use crate::setup; use std::fmt; use std::collections::BTreeMap; diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index 512098d9cc..408737525b 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -136,4 +136,4 @@ impl Serialize for HexColor { #[cfg(test)] #[path = "./unit/theme_test.rs"] -mod layout_test; +mod theme_test; diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index 2d4ed39447..c69960776d 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -3,6 +3,7 @@ use super::super::keybinds::*; use crate::input::config::Config; use crate::data::{self, CharOrArrow, Key}; use insta::assert_snapshot; +use strum::IntoEnumIterator; #[test] fn can_define_keybindings_in_configfile() { diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 2954030ac5..0fc41c1edb 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -1,6 +1,4 @@ -use crate::input::config::ConfigError; use super::super::layout::*; -use std::convert::TryInto; use insta::assert_snapshot; #[test] 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 new file mode 100644 index 0000000000..26ee77f898 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap @@ -0,0 +1,109 @@ +--- +source: zellij-utils/src/input/./unit/theme_test.rs +assertion_line: 15 +expression: "format!(\"{:#?}\", theme)" +--- +( + "dracula", + Theme { + palette: Palette { + source: Default, + theme_hue: Dark, + fg: Rgb( + ( + 248, + 248, + 242, + ), + ), + bg: Rgb( + ( + 40, + 42, + 54, + ), + ), + black: Rgb( + ( + 0, + 0, + 0, + ), + ), + red: Rgb( + ( + 255, + 85, + 85, + ), + ), + green: Rgb( + ( + 80, + 250, + 123, + ), + ), + yellow: Rgb( + ( + 241, + 250, + 140, + ), + ), + blue: Rgb( + ( + 98, + 114, + 164, + ), + ), + magenta: Rgb( + ( + 255, + 121, + 198, + ), + ), + cyan: Rgb( + ( + 139, + 233, + 253, + ), + ), + white: Rgb( + ( + 255, + 255, + 255, + ), + ), + orange: Rgb( + ( + 255, + 184, + 108, + ), + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + }, +) diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index f361271bfe..cf38718c23 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -10,7 +10,7 @@ use crate::{ options::Options, }, }; -use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; +use crate::input::theme::Theme; use clap::{Args, IntoApp}; use clap_complete::Shell; use directories_next::BaseDirs; From 07d6f7f2cee6782fda1d7726b436922ed865c535 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 15 Sep 2022 11:58:33 +0200 Subject: [PATCH 31/55] feat(cli): send actions through the cli --- src/commands.rs | 67 +- src/main.rs | 5 +- zellij-client/src/cli_client.rs | 33 + zellij-client/src/fake_client.rs | 184 -- zellij-client/src/lib.rs | 2 +- zellij-server/src/lib.rs | 16 +- zellij-server/src/os_input_output.rs | 1 + zellij-server/src/route.rs | 60 +- zellij-server/src/screen.rs | 333 +++- zellij-server/src/tab/mod.rs | 11 +- zellij-server/src/thread_bus.rs | 6 + .../unit/fixtures/layout-with-three-panes.kdl | 5 + zellij-server/src/unit/screen_tests.rs | 1730 ++++++++++++++++- ...n_tests__send_cli_close_pane_action-2.snap | 16 + ...n_tests__send_cli_close_pane_action-3.snap | 16 + ...n_tests__send_cli_close_pane_action-4.snap | 6 + ...een_tests__send_cli_close_pane_action.snap | 16 + ...en_tests__send_cli_close_tab_action-2.snap | 16 + ...en_tests__send_cli_close_tab_action-3.snap | 16 + ...en_tests__send_cli_close_tab_action-4.snap | 16 + ...en_tests__send_cli_close_tab_action-5.snap | 16 + ...en_tests__send_cli_close_tab_action-6.snap | 16 + ...en_tests__send_cli_close_tab_action-7.snap | 6 + ...reen_tests__send_cli_close_tab_action.snap | 16 + ...en_tests__send_cli_dump_screen_action.snap | 6 + ...i_edit_action_with_default_parameters.snap | 6 + ...send_cli_edit_action_with_line_number.snap | 6 + ..._cli_edit_action_with_split_direction.snap | 6 + ...ts__send_cli_focus_next_pane_action-2.snap | 6 + ...ts__send_cli_focus_next_pane_action-3.snap | 6 + ...ts__send_cli_focus_next_pane_action-4.snap | 6 + ...ests__send_cli_focus_next_pane_action.snap | 6 + ...send_cli_focus_previous_pane_action-2.snap | 6 + ...send_cli_focus_previous_pane_action-3.snap | 6 + ...send_cli_focus_previous_pane_action-4.snap | 6 + ...__send_cli_focus_previous_pane_action.snap | 6 + ...o_next_and_previous_search_occurrence.snap | 6 + ...een_tests__send_cli_goto_tab_action-2.snap | 16 + ...een_tests__send_cli_goto_tab_action-3.snap | 16 + ...een_tests__send_cli_goto_tab_action-4.snap | 16 + ...een_tests__send_cli_goto_tab_action-5.snap | 16 + ...een_tests__send_cli_goto_tab_action-6.snap | 16 + ...een_tests__send_cli_goto_tab_action-7.snap | 6 + ...creen_tests__send_cli_goto_tab_action.snap | 16 + ...nd_cli_half_page_scroll_down_action-2.snap | 16 + ...nd_cli_half_page_scroll_down_action-3.snap | 16 + ...nd_cli_half_page_scroll_down_action-4.snap | 16 + ...nd_cli_half_page_scroll_down_action-5.snap | 16 + ...nd_cli_half_page_scroll_down_action-6.snap | 6 + ...send_cli_half_page_scroll_down_action.snap | 16 + ...send_cli_half_page_scroll_up_action-2.snap | 16 + ...send_cli_half_page_scroll_up_action-3.snap | 16 + ...send_cli_half_page_scroll_up_action-4.snap | 6 + ...__send_cli_half_page_scroll_up_action.snap | 16 + ...d_cli_move_focus_or_tab_pane_action-2.snap | 6 + ...d_cli_move_focus_or_tab_pane_action-3.snap | 6 + ...d_cli_move_focus_or_tab_pane_action-4.snap | 6 + ...end_cli_move_focus_or_tab_pane_action.snap | 6 + ...ts__send_cli_move_focus_pane_action-2.snap | 6 + ...ts__send_cli_move_focus_pane_action-3.snap | 6 + ...ts__send_cli_move_focus_pane_action-4.snap | 6 + ...ests__send_cli_move_focus_pane_action.snap | 6 + ...en_tests__send_cli_move_pane_action-2.snap | 26 + ...en_tests__send_cli_move_pane_action-3.snap | 26 + ...en_tests__send_cli_move_pane_action-4.snap | 6 + ...reen_tests__send_cli_move_pane_action.snap | 26 + ...pane_action_with_command_cwd_and_args.snap | 6 + ...w_pane_action_with_default_parameters.snap | 6 + ..._new_pane_action_with_split_direction.snap | 6 + ...end_cli_new_tab_action_default_params.snap | 6 + ...i_new_tab_action_with_name_and_layout.snap | 74 + ...een_tests__send_cli_next_tab_action-2.snap | 16 + ...een_tests__send_cli_next_tab_action-3.snap | 16 + ...een_tests__send_cli_next_tab_action-4.snap | 16 + ...een_tests__send_cli_next_tab_action-5.snap | 16 + ...een_tests__send_cli_next_tab_action-6.snap | 16 + ...een_tests__send_cli_next_tab_action-7.snap | 6 + ...creen_tests__send_cli_next_tab_action.snap | 16 + ...s__send_cli_page_scroll_down_action-2.snap | 16 + ...s__send_cli_page_scroll_down_action-3.snap | 16 + ...s__send_cli_page_scroll_down_action-4.snap | 16 + ...s__send_cli_page_scroll_down_action-5.snap | 16 + ...s__send_cli_page_scroll_down_action-6.snap | 6 + ...sts__send_cli_page_scroll_down_action.snap | 16 + ...sts__send_cli_page_scroll_up_action-2.snap | 16 + ...sts__send_cli_page_scroll_up_action-3.snap | 16 + ...sts__send_cli_page_scroll_up_action-4.snap | 6 + ...tests__send_cli_page_scroll_up_action.snap | 16 + ...tests__send_cli_previous_tab_action-2.snap | 16 + ...tests__send_cli_previous_tab_action-3.snap | 16 + ...tests__send_cli_previous_tab_action-4.snap | 16 + ...tests__send_cli_previous_tab_action-5.snap | 16 + ...tests__send_cli_previous_tab_action-6.snap | 16 + ...tests__send_cli_previous_tab_action-7.snap | 6 + ...n_tests__send_cli_previous_tab_action.snap | 16 + ..._tests__send_cli_rename_pane_action-2.snap | 16 + ..._tests__send_cli_rename_pane_action-3.snap | 16 + ..._tests__send_cli_rename_pane_action-4.snap | 6 + ...en_tests__send_cli_rename_pane_action.snap | 16 + ...en__screen_tests__send_cli_rename_tab.snap | 436 +++++ ...s__send_cli_resize_action_to_screen-2.snap | 26 + ...s__send_cli_resize_action_to_screen-3.snap | 26 + ...sts__send_cli_resize_action_to_screen.snap | 6 + ..._tests__send_cli_scroll_down_action-2.snap | 16 + ..._tests__send_cli_scroll_down_action-3.snap | 16 + ..._tests__send_cli_scroll_down_action-4.snap | 16 + ..._tests__send_cli_scroll_down_action-5.snap | 16 + ..._tests__send_cli_scroll_down_action-6.snap | 16 + ..._tests__send_cli_scroll_down_action-7.snap | 16 + ..._tests__send_cli_scroll_down_action-8.snap | 16 + ..._tests__send_cli_scroll_down_action-9.snap | 6 + ...en_tests__send_cli_scroll_down_action.snap | 16 + ...s__send_cli_scroll_to_bottom_action-2.snap | 16 + ...s__send_cli_scroll_to_bottom_action-3.snap | 16 + ...s__send_cli_scroll_to_bottom_action-4.snap | 16 + ...s__send_cli_scroll_to_bottom_action-5.snap | 16 + ...s__send_cli_scroll_to_bottom_action-6.snap | 16 + ...s__send_cli_scroll_to_bottom_action-7.snap | 16 + ...s__send_cli_scroll_to_bottom_action-8.snap | 6 + ...sts__send_cli_scroll_to_bottom_action.snap | 16 + ...en_tests__send_cli_scroll_up_action-2.snap | 16 + ...en_tests__send_cli_scroll_up_action-3.snap | 16 + ...en_tests__send_cli_scroll_up_action-4.snap | 16 + ...en_tests__send_cli_scroll_up_action-5.snap | 6 + ...reen_tests__send_cli_scroll_up_action.snap | 16 + ...en_tests__send_cli_switch_mode_action.snap | 6 + ...end_cli_toggle_active_tab_sync_action.snap | 6 + ...sts__send_cli_toggle_floating_panes-2.snap | 16 + ...sts__send_cli_toggle_floating_panes-3.snap | 16 + ...sts__send_cli_toggle_floating_panes-4.snap | 16 + ...sts__send_cli_toggle_floating_panes-5.snap | 16 + ...sts__send_cli_toggle_floating_panes-6.snap | 6 + ...tests__send_cli_toggle_floating_panes.snap | 16 + ..._send_cli_toggle_full_screen_action-2.snap | 16 + ..._send_cli_toggle_full_screen_action-3.snap | 16 + ..._send_cli_toggle_full_screen_action-4.snap | 6 + ...s__send_cli_toggle_full_screen_action.snap | 16 + ...send_cli_toggle_pane_embed_or_float-2.snap | 16 + ...send_cli_toggle_pane_embed_or_float-3.snap | 16 + ...send_cli_toggle_pane_embed_or_float-4.snap | 16 + ...send_cli_toggle_pane_embed_or_float-5.snap | 6 + ...__send_cli_toggle_pane_embed_or_float.snap | 16 + ..._send_cli_toggle_pane_frames_action-2.snap | 16 + ..._send_cli_toggle_pane_frames_action-3.snap | 16 + ..._send_cli_toggle_pane_frames_action-4.snap | 6 + ...s__send_cli_toggle_pane_frames_action.snap | 16 + ...s__send_cli_undo_rename_pane_action-2.snap | 16 + ...s__send_cli_undo_rename_pane_action-3.snap | 16 + ...s__send_cli_undo_rename_pane_action-4.snap | 16 + ...s__send_cli_undo_rename_pane_action-5.snap | 6 + ...sts__send_cli_undo_rename_pane_action.snap | 16 + ...creen_tests__send_cli_undo_rename_tab.snap | 473 +++++ ...en_tests__send_cli_update_search_term.snap | 6 + ...ests__send_cli_write_action_to_screen.snap | 6 + ...send_cli_write_chars_action_to_screen.snap | 6 + zellij-utils/src/cli.rs | 116 +- zellij-utils/src/data.rs | 7 +- zellij-utils/src/envs.rs | 11 + zellij-utils/src/errors.rs | 4 + zellij-utils/src/input/actions.rs | 147 +- zellij-utils/src/input/config.rs | 8 + ..._error_received_on_unknown_input_mode.snap | 4 +- 162 files changed, 5129 insertions(+), 378 deletions(-) create mode 100644 zellij-client/src/cli_client.rs delete mode 100644 zellij-client/src/fake_client.rs create mode 100644 zellij-server/src/unit/fixtures/layout-with-three-panes.kdl create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-7.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_dump_screen_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-7.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-7.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-7.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-7.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-8.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-9.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-7.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-8.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_switch_mode_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-6.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-2.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-3.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-4.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap diff --git a/src/commands.rs b/src/commands.rs index f47c60ab97..40863feec1 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -9,6 +9,7 @@ use dialoguer::Confirm; use miette::Result; use std::path::PathBuf; use std::process; +use zellij_utils::input::actions::Action; use zellij_client::start_client as start_client_impl; use zellij_client::{os_input_output::get_client_os_input, ClientInfo}; use zellij_server::os_input_output::get_server_os_input; @@ -118,22 +119,20 @@ fn find_indexed_session( } } -/// Send a vec of `[Action]` to a currently running session. -#[cfg(feature = "unstable")] -pub(crate) fn send_action_to_session(opts: zellij_utils::cli::CliArgs) { +pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, session_name: Option) { match get_active_session() { ActiveSession::None => { eprintln!("There is no active session!"); std::process::exit(1); }, ActiveSession::One(session_name) => { - attach_with_fake_client(opts, &session_name); + attach_with_cli_client(cli_action, &session_name); }, ActiveSession::Many => { - if let Some(session_name) = opts.session.clone() { - attach_with_fake_client(opts, &session_name); + if let Some(session_name) = session_name { + attach_with_cli_client(cli_action, &session_name); } else if let Ok(session_name) = envs::get_session_name() { - attach_with_fake_client(opts, &session_name); + attach_with_cli_client(cli_action, &session_name); } else { println!("Please specify the session name to send actions to. The following sessions are active:"); print_sessions(get_sessions().unwrap()); @@ -143,44 +142,22 @@ pub(crate) fn send_action_to_session(opts: zellij_utils::cli::CliArgs) { }; } -#[cfg(feature = "unstable")] -fn attach_with_fake_client(opts: zellij_utils::cli::CliArgs, name: &str) { - if let Some(zellij_utils::cli::Command::Sessions(zellij_utils::cli::Sessions::Action { - action: Some(action), - })) = opts.command.clone() - { - let action = format!("[{}]", action); - match zellij_utils::serde_yaml::from_str::(&action).into_diagnostic() { - Ok(parsed) => { - let (config, _, config_options) = match Setup::from_cli_args(&opts) { - Ok(results) => results, - Err(e) => { - eprintln!("{}", e); - process::exit(1); - }, - }; - let os_input = get_os_input(zellij_client::os_input_output::get_client_os_input); - - let actions = parsed.actions().to_vec(); - log::debug!("Starting fake Zellij client!"); - zellij_client::fake_client::start_fake_client( - Box::new(os_input), - opts, - *Box::new(config), - config_options, - ClientInfo::New(name.to_string()), - None, - actions, - ); - log::debug!("Quitting fake client now."); - std::process::exit(0); - }, - Err(e) => { - eprintln!("{:?}", e); - std::process::exit(1); - }, - }; - }; +fn attach_with_cli_client(cli_action: zellij_utils::cli::CliAction, session_name: &str) { + let os_input = get_os_input(zellij_client::os_input_output::get_client_os_input); + match Action::actions_from_cli(cli_action) { + Ok(actions) => { + zellij_client::cli_client::start_cli_client( + Box::new(os_input), + session_name, + actions, + ); + std::process::exit(0); + } + Err(e) => { + log::error!("Error sending action: {}", e); + std::process::exit(2); + } + } } fn attach_with_session_index(config_options: Options, index: usize, create: bool) -> ClientInfo { diff --git a/src/main.rs b/src/main.rs index ca05dbf10f..f503e8492e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,10 +14,9 @@ fn main() { configure_logger(); let opts = CliArgs::parse(); - #[cfg(feature = "unstable")] { - if let Some(Command::Sessions(Sessions::Action { .. })) = opts.command { - commands::send_action_to_session(opts); + if let Some(Command::Sessions(Sessions::Action(cli_action))) = opts.command { + commands::send_action_to_session(cli_action, opts.session); std::process::exit(0); } } diff --git a/zellij-client/src/cli_client.rs b/zellij-client/src/cli_client.rs new file mode 100644 index 0000000000..7da09243ee --- /dev/null +++ b/zellij-client/src/cli_client.rs @@ -0,0 +1,33 @@ +//! The `[cli_client]` is used to attach to a running server session +//! and dispatch actions, that are specified through the command line. +use std::{fs, path::PathBuf}; +use zellij_utils::envs; + +use crate::{ + os_input_output::ClientOsApi, +}; +use zellij_utils::{ + data::ClientId, + input::actions::Action, + ipc::ClientToServerMsg, +}; + +pub fn start_cli_client( + os_input: Box, + session_name: &str, + actions: Vec, +) { + let client_id: Option = envs::get_client_id().ok().map(|s| s.encode_utf16().next().unwrap()); // TODO: better? does this even work? + let zellij_ipc_pipe: PathBuf = { + let mut sock_dir = zellij_utils::consts::ZELLIJ_SOCK_DIR.clone(); + fs::create_dir_all(&sock_dir).unwrap(); + zellij_utils::shared::set_permissions(&sock_dir, 0o700).unwrap(); + sock_dir.push(session_name); + sock_dir + }; + os_input.connect_to_server(&*zellij_ipc_pipe); + for action in actions { + let msg = ClientToServerMsg::Action(action, client_id); + os_input.send_to_server(msg); + } +} diff --git a/zellij-client/src/fake_client.rs b/zellij-client/src/fake_client.rs deleted file mode 100644 index 9e24e2a5cd..0000000000 --- a/zellij-client/src/fake_client.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! The `[fake_client]` is used to attach to a running server session -//! and dispatch actions, that are specified through the command line. -//! Multiple actions at the same time can be dispatched. -use log::debug; -use std::sync::{Arc, Mutex}; -use std::{fs, path::PathBuf, thread}; - -use crate::{ - command_is_executing::CommandIsExecuting, input_handler::input_actions, - os_input_output::ClientOsApi, stdin_ansi_parser::StdinAnsiParser, stdin_handler::stdin_loop, - ClientInfo, ClientInstruction, InputInstruction, -}; -use zellij_utils::{ - channels::{self, ChannelWithContext, SenderWithContext}, - cli::CliArgs, - data::{ClientId, Style}, - errors::ContextType, - input::{actions::Action, config::Config, layout::Layout, options::Options}, - ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg}, -}; - -pub fn start_fake_client( - os_input: Box, - _opts: CliArgs, - config: Config, - config_options: Options, - info: ClientInfo, - _layout: Option, - actions: Vec, -) { - unimplemented!() -// debug!("Starting fake Zellij client!"); -// let session_name = info.get_session_name(); -// -// // TODO: Ideally the `fake_client` would not need to specify these options, -// // but the `[NewTab:]` action depends on this state being -// // even in this client. -// let palette = config.themes.clone().map_or_else( -// || os_input.load_palette(), -// |t| { -// t.theme_config(&config_options) -// .unwrap_or_else(|| os_input.load_palette()) -// }, -// ); -// -// let full_screen_ws = os_input.get_terminal_size_using_fd(0); -// let client_attributes = ClientAttributes { -// size: full_screen_ws, -// style: Style { -// colors: palette, -// rounded_corners: config.ui.unwrap_or_default().pane_frames.rounded_corners, -// }, -// }; -// -// let first_msg = ClientToServerMsg::AttachClient(client_attributes, config_options.clone()); -// -// let zellij_ipc_pipe: PathBuf = { -// let mut sock_dir = zellij_utils::consts::ZELLIJ_SOCK_DIR.clone(); -// fs::create_dir_all(&sock_dir).unwrap(); -// zellij_utils::shared::set_permissions(&sock_dir, 0o700).unwrap(); -// sock_dir.push(session_name); -// sock_dir -// }; -// os_input.connect_to_server(&*zellij_ipc_pipe); -// os_input.send_to_server(first_msg); -// -// let mut command_is_executing = CommandIsExecuting::new(); -// -// let (send_client_instructions, receive_client_instructions): ChannelWithContext< -// ClientInstruction, -// > = channels::bounded(50); -// let send_client_instructions = SenderWithContext::new(send_client_instructions); -// -// let (send_input_instructions, receive_input_instructions): ChannelWithContext< -// InputInstruction, -// > = channels::bounded(50); -// let send_input_instructions = SenderWithContext::new(send_input_instructions); -// -// std::panic::set_hook({ -// use zellij_utils::errors::handle_panic; -// let send_client_instructions = send_client_instructions.clone(); -// Box::new(move |info| { -// handle_panic(info, &send_client_instructions); -// }) -// }); -// -// let stdin_ansi_parser = Arc::new(Mutex::new(StdinAnsiParser::new())); -// let _stdin_thread = thread::Builder::new() -// .name("stdin_handler".to_string()) -// .spawn({ -// let os_input = os_input.clone(); -// let send_input_instructions = send_input_instructions.clone(); -// move || stdin_loop(os_input, send_input_instructions, stdin_ansi_parser) -// }); -// -// let clients: Vec; -// os_input.send_to_server(ClientToServerMsg::ListClients); -// #[allow(clippy::collapsible_match)] -// loop { -// if let Some((msg, _)) = os_input.recv_from_server() { -// if let ServerToClientMsg::ActiveClients(active_clients) = msg { -// clients = active_clients; -// break; -// } -// } -// } -// debug!("The connected client id's are: {:?}.", clients); -// -// let _input_thread = thread::Builder::new() -// .name("input_handler".to_string()) -// .spawn({ -// let send_client_instructions = send_client_instructions.clone(); -// let command_is_executing = command_is_executing.clone(); -// let os_input = os_input.clone(); -// let default_mode = config_options.default_mode.unwrap_or_default(); -// let session_name = session_name.to_string(); -// move || { -// input_actions( -// os_input, -// config, -// config_options, -// command_is_executing, -// clients, -// send_client_instructions, -// default_mode, -// receive_input_instructions, -// actions, -// session_name, -// ) -// } -// }); -// -// let router_thread = thread::Builder::new() -// .name("router".to_string()) -// .spawn({ -// let os_input = os_input.clone(); -// let mut should_break = false; -// move || loop { -// if let Some((instruction, err_ctx)) = os_input.recv_from_server() { -// err_ctx.update_thread_ctx(); -// if let ServerToClientMsg::Exit(_) = instruction { -// should_break = true; -// } -// send_client_instructions.send(instruction.into()).unwrap(); -// if should_break { -// break; -// } -// } -// } -// }) -// .unwrap(); -// -// loop { -// let (client_instruction, mut err_ctx) = receive_client_instructions -// .recv() -// .expect("failed to receive app instruction on channel"); -// -// err_ctx.add_call(ContextType::Client((&client_instruction).into())); -// match client_instruction { -// ClientInstruction::Exit(_) => { -// os_input.send_to_server(ClientToServerMsg::ClientExited); -// break; -// }, -// ClientInstruction::Error(_) => { -// let _ = os_input.send_to_server(ClientToServerMsg::Action(Action::Quit, None)); -// // handle_error(backtrace); -// }, -// ClientInstruction::Render(_) => { -// // This is a fake client, that doesn't render, but -// // dispatches actions. -// }, -// ClientInstruction::UnblockInputThread => { -// command_is_executing.unblock_input_thread(); -// }, -// ClientInstruction::SwitchToMode(input_mode) => { -// send_input_instructions -// .send(InputInstruction::SwitchToMode(input_mode)) -// .unwrap(); -// }, -// _ => {}, -// } -// } -// router_thread.join().unwrap(); -} diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 062838d789..52e5fd1f48 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -1,7 +1,7 @@ pub mod os_input_output; mod command_is_executing; -pub mod fake_client; +pub mod cli_client; mod input_handler; mod sessions; mod stdin_ansi_parser; diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index a5d3b85d20..212881e7aa 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -118,10 +118,18 @@ impl Drop for SessionMetaData { let _ = self.senders.send_to_screen(ScreenInstruction::Exit); let _ = self.senders.send_to_plugin(PluginInstruction::Exit); let _ = self.senders.send_to_pty_writer(PtyWriteInstruction::Exit); - let _ = self.screen_thread.take().unwrap().join(); - let _ = self.pty_thread.take().unwrap().join(); - let _ = self.wasm_thread.take().unwrap().join(); - let _ = self.pty_writer_thread.take().unwrap().join(); + if let Some(screen_thread) = self.screen_thread.take() { + let _ = screen_thread.join(); + } + if let Some(pty_thread) = self.pty_thread.take() { + let _ = pty_thread.join(); + } + if let Some(wasm_thread) = self.wasm_thread.take() { + let _ = wasm_thread.join(); + } + if let Some(pty_writer_thread) = self.pty_writer_thread.take() { + let _ = pty_writer_thread.join(); + } } } diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs index 8bd62a2193..728ee9b63a 100644 --- a/zellij-server/src/os_input_output.rs +++ b/zellij-server/src/os_input_output.rs @@ -198,6 +198,7 @@ pub fn spawn_terminal( quit_cb: Box, default_editor: Option, ) -> Result<(RawFd, RawFd), &'static str> { + log::info!("terminal_action: {:?}", terminal_action); let mut failover_cmd_args = None; let cmd = match terminal_action { TerminalAction::OpenFile(file_to_open, line_number) => { diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 88ef67c513..8f22afb6eb 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -20,7 +20,7 @@ use zellij_utils::{ use crate::ClientId; -fn route_action( +pub(crate) fn route_action( action: Action, session: &SessionMetaData, _os_input: &dyn ServerOsApi, @@ -248,6 +248,64 @@ fn route_action( }; session.senders.send_to_pty(pty_instr).unwrap(); }, + Action::EditFile(path_to_file, line_number, split_direction, should_float) => { + match should_float { + Some(true) => { + session + .senders + .send_to_screen(ScreenInstruction::ShowFloatingPanes(client_id)) + .unwrap(); + + }, + Some(false) => { + session + .senders + .send_to_screen(ScreenInstruction::HideFloatingPanes(client_id)) + .unwrap(); + }, + None => {} + }; + + let open_file = TerminalAction::OpenFile(path_to_file, line_number); + let pty_instr = match (split_direction, should_float.unwrap_or(false)) { + (Some(Direction::Left), false) => PtyInstruction::SpawnTerminalVertically(Some(open_file), client_id), + (Some(Direction::Right), false) => PtyInstruction::SpawnTerminalVertically(Some(open_file), client_id), + (Some(Direction::Up), false) => PtyInstruction::SpawnTerminalHorizontally(Some(open_file), client_id), + (Some(Direction::Down), false) => { + PtyInstruction::SpawnTerminalHorizontally(Some(open_file), client_id) + }, + // No direction specified or should float - defer placement to screen + (None, _) | (_, true) => PtyInstruction::SpawnTerminal(Some(open_file), ClientOrTabIndex::ClientId(client_id)), + }; + session.senders.send_to_pty(pty_instr).unwrap(); + }, + Action::SwitchModeForAllClients(input_mode) => { + let attrs = &session.client_attributes; + session + .senders + .send_to_plugin(PluginInstruction::Update( + None, + None, + Event::ModeUpdate(get_mode_info(input_mode, attrs, session.capabilities)), + )) + .unwrap(); + session + .senders + .send_to_screen(ScreenInstruction::ChangeModeForAllClients( + get_mode_info(input_mode, attrs, session.capabilities) + )) + .unwrap(); + }, + Action::NewFloatingPane(run_command) => { + session + .senders + .send_to_screen(ScreenInstruction::ShowFloatingPanes(client_id)) + .unwrap(); + let run_cmd = run_command.map(|cmd| TerminalAction::RunCommand(cmd.into())).or_else(|| { + session.default_shell.clone() + }); + session.senders.send_to_pty(PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id))).unwrap(); + }, Action::TogglePaneEmbedOrFloating => { session .senders diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 5d64186a22..947e5ed3c6 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -28,7 +28,7 @@ use zellij_utils::{ data::{Event, InputMode, ModeInfo, Palette, PaletteColor, PluginCapabilities, Style, TabInfo}, errors::{ContextType, ScreenContext}, input::{get_mode_info, options::Options}, - ipc::{ClientAttributes, PixelDimensions}, + ipc::{ClientAttributes, PixelDimensions, ServerToClientMsg}, }; /// Get the active tab and call a closure on it @@ -40,6 +40,7 @@ use zellij_utils::{ /// - screen: An instance of `Screen` to operate on /// - client_id: The client_id, usually taken from the `ScreenInstruction` that's being processed /// - closure: A closure satisfying `|tab: &mut Tab| -> ()` + macro_rules! active_tab { ($screen:ident, $client_id:ident, $closure:expr) => { if let Some(active_tab) = $screen.get_active_tab_mut($client_id) { @@ -53,6 +54,27 @@ macro_rules! active_tab { } }; } +macro_rules! active_tab_and_connected_client_id { + ($screen:ident, $client_id:ident, $closure:expr) => { + match $screen.get_active_tab_mut($client_id) { + Some(active_tab) => { + $closure(active_tab, $client_id); + } + None => { + if let Some(client_id) = $screen.get_first_client_id() { + match $screen.get_active_tab_mut(client_id) { + Some(active_tab) => { + $closure(active_tab, client_id); + } + None => { + log::error!("Active tab not found for client id: {:?}", $client_id); + } + } + }; + } + } + }; +} /// Instructions that can be sent to the [`Screen`]. #[derive(Debug, Clone)] @@ -63,6 +85,8 @@ pub enum ScreenInstruction { OpenInPlaceEditor(PaneId, ClientId), TogglePaneEmbedOrFloating(ClientId), ToggleFloatingPanes(ClientId, Option), + ShowFloatingPanes(ClientId), + HideFloatingPanes(ClientId), HorizontalSplit(PaneId, ClientId), VerticalSplit(PaneId, ClientId), WriteCharacter(Vec, ClientId), @@ -121,6 +145,7 @@ pub enum ScreenInstruction { TerminalForegroundColor(String), TerminalColorRegisters(Vec<(usize, String)>), ChangeMode(ModeInfo, ClientId), + ChangeModeForAllClients(ModeInfo), LeftClick(Position, ClientId), RightClick(Position, ClientId), MiddleClick(Position, ClientId), @@ -156,6 +181,8 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenContext::TogglePaneEmbedOrFloating }, ScreenInstruction::ToggleFloatingPanes(..) => ScreenContext::ToggleFloatingPanes, + ScreenInstruction::ShowFloatingPanes(..) => ScreenContext::ShowFloatingPanes, + ScreenInstruction::HideFloatingPanes(..) => ScreenContext::HideFloatingPanes, ScreenInstruction::HorizontalSplit(..) => ScreenContext::HorizontalSplit, ScreenInstruction::VerticalSplit(..) => ScreenContext::VerticalSplit, ScreenInstruction::WriteCharacter(..) => ScreenContext::WriteCharacter, @@ -222,6 +249,7 @@ impl From<&ScreenInstruction> for ScreenContext { }, ScreenInstruction::TerminalColorRegisters(..) => ScreenContext::TerminalColorRegisters, ScreenInstruction::ChangeMode(..) => ScreenContext::ChangeMode, + ScreenInstruction::ChangeModeForAllClients(..) => ScreenContext::ChangeModeForAllClients, ScreenInstruction::ToggleActiveSyncTab(..) => ScreenContext::ToggleActiveSyncTab, ScreenInstruction::ScrollUpAt(..) => ScreenContext::ScrollUpAt, ScreenInstruction::ScrollDownAt(..) => ScreenContext::ScrollDownAt, @@ -464,28 +492,42 @@ impl Screen { /// Sets this [`Screen`]'s active [`Tab`] to the next tab. pub fn switch_tab_next(&mut self, client_id: ClientId) { - if let Some(active_tab) = self.get_active_tab(client_id) { - let active_tab_pos = active_tab.position; - let new_tab_pos = (active_tab_pos + 1) % self.tabs.len(); - self.switch_active_tab(new_tab_pos, client_id); + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) } else { - log::error!("Active tab not found for client_id: {:?}", client_id); + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + if let Some(active_tab) = self.get_active_tab(client_id) { + let active_tab_pos = active_tab.position; + let new_tab_pos = (active_tab_pos + 1) % self.tabs.len(); + self.switch_active_tab(new_tab_pos, client_id); + } else { + log::error!("Active tab not found for client_id: {:?}", client_id); + } } } /// Sets this [`Screen`]'s active [`Tab`] to the previous tab. pub fn switch_tab_prev(&mut self, client_id: ClientId) { - if let Some(active_tab) = self.get_active_tab(client_id) { - let active_tab_pos = active_tab.position; - let new_tab_pos = if active_tab_pos == 0 { - self.tabs.len() - 1 - } else { - active_tab_pos - 1 - }; - - self.switch_active_tab(new_tab_pos, client_id); + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) } else { - log::error!("Active tab not found for client_id: {:?}", client_id); + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + if let Some(active_tab) = self.get_active_tab(client_id) { + let active_tab_pos = active_tab.position; + let new_tab_pos = if active_tab_pos == 0 { + self.tabs.len() - 1 + } else { + active_tab_pos - 1 + }; + + self.switch_active_tab(new_tab_pos, client_id); + } else { + log::error!("Active tab not found for client_id: {:?}", client_id); + } } } @@ -530,8 +572,15 @@ impl Screen { // Closes the client_id's focused tab pub fn close_tab(&mut self, client_id: ClientId) { - let active_tab_index = *self.active_tab_indices.get(&client_id).unwrap(); - self.close_tab_at_index(active_tab_index); + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) + } else { + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + let active_tab_index = *self.active_tab_indices.get(&client_id).unwrap(); + self.close_tab_at_index(active_tab_index); + } } pub fn resize_to_screen(&mut self, new_screen_size: Size) { @@ -619,6 +668,10 @@ impl Screen { } } + pub fn get_first_client_id(&self) -> Option { + self.active_tab_indices.keys().next().copied() + } + /// Returns an immutable reference to this [`Screen`]'s previous active [`Tab`]. /// Consumes the last entry in tab history. pub fn get_previous_tab(&mut self, client_id: ClientId) -> Option<&Tab> { @@ -649,6 +702,13 @@ impl Screen { /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. pub fn new_tab(&mut self, layout: PaneLayout, new_pids: Vec, client_id: ClientId) { + let client_id = if self.get_active_tab(client_id).is_some() { + client_id + } else if let Some(first_client_id) = self.get_first_client_id() { + first_client_id + } else { + client_id + }; let tab_index = self.get_new_tab_index(); let position = self.tabs.len(); let mut tab = Tab::new( @@ -788,36 +848,50 @@ impl Screen { } pub fn update_active_tab_name(&mut self, buf: Vec, client_id: ClientId) { - let s = str::from_utf8(&buf).unwrap(); - if let Some(active_tab) = self.get_active_tab_mut(client_id) { - match s { - "\0" => { - active_tab.name = String::new(); - }, - "\u{007F}" | "\u{0008}" => { - // delete and backspace keys - active_tab.name.pop(); - }, - c => { - // It only allows printable unicode - if buf.iter().all(|u| matches!(u, 0x20..=0x7E)) { - active_tab.name.push_str(c); - } - }, - } - self.update_tabs(); + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) } else { - log::error!("Active tab not found for client id: {:?}", client_id); + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + let s = str::from_utf8(&buf).unwrap(); + if let Some(active_tab) = self.get_active_tab_mut(client_id) { + match s { + "\0" => { + active_tab.name = String::new(); + }, + "\u{007F}" | "\u{0008}" => { + // delete and backspace keys + active_tab.name.pop(); + }, + c => { + // It only allows printable unicode + if buf.iter().all(|u| matches!(u, 0x20..=0x7E)) { + active_tab.name.push_str(c); + } + }, + } + self.update_tabs(); + } else { + log::error!("Active tab not found for client id: {:?}", client_id); + } } } pub fn undo_active_rename_tab(&mut self, client_id: ClientId) { - if let Some(active_tab) = self.get_active_tab_mut(client_id) { - if active_tab.name != active_tab.prev_name { - active_tab.name = active_tab.prev_name.clone(); - self.update_tabs(); - } + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) } else { - log::error!("Active tab not found for client id: {:?}", client_id); + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + if let Some(active_tab) = self.get_active_tab_mut(client_id) { + if active_tab.name != active_tab.prev_name { + active_tab.name = active_tab.prev_name.clone(); + self.update_tabs(); + } + } else { + log::error!("Active tab not found for client id: {:?}", client_id); + } } } pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) { @@ -866,22 +940,48 @@ impl Screen { tab.mark_active_pane_for_rerender(client_id); } } - pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) { - if let Some(active_tab) = self.get_active_tab_mut(client_id) { - if !active_tab.move_focus_left(client_id) { - self.switch_tab_prev(client_id); + pub fn change_mode_for_all_clients(&mut self, mode_info: ModeInfo) { + let connected_client_ids: Vec = self.active_tab_indices.keys().copied().collect(); + for client_id in connected_client_ids { + self.change_mode(mode_info.clone(), client_id); + if let Some(os_input) = &mut self.bus.os_input { + let _ = os_input.send_to_client( + client_id, + ServerToClientMsg::SwitchToMode(mode_info.mode), + ); } + } + } + pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) { + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) } else { - log::error!("Active tab not found for client id: {:?}", client_id); + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + if let Some(active_tab) = self.get_active_tab_mut(client_id) { + if !active_tab.move_focus_left(client_id) { + self.switch_tab_prev(client_id); + } + } else { + log::error!("Active tab not found for client id: {:?}", client_id); + } } } pub fn move_focus_right_or_next_tab(&mut self, client_id: ClientId) { - if let Some(active_tab) = self.get_active_tab_mut(client_id) { - if !active_tab.move_focus_right(client_id) { - self.switch_tab_next(client_id); - } + let client_id = if self.get_active_tab(client_id).is_some() { + Some(client_id) } else { - log::error!("Active tab not found for client id: {:?}", client_id); + self.get_first_client_id() + }; + if let Some(client_id) = client_id { + if let Some(active_tab) = self.get_active_tab_mut(client_id) { + if !active_tab.move_focus_right(client_id) { + self.switch_tab_next(client_id); + } + } else { + log::error!("Active tab not found for client id: {:?}", client_id); + } } } pub fn toggle_tab(&mut self, client_id: ClientId) { @@ -912,7 +1012,6 @@ pub(crate) fn screen_thread_main( client_attributes: ClientAttributes, config_options: Box, ) { - // let mut scrollbacks: HashMap = HashMap::new(); let capabilities = config_options.simplified_ui; let draw_pane_frames = config_options.pane_frames.unwrap_or(true); let session_is_mirrored = config_options.mirror_session.unwrap_or(false); @@ -961,7 +1060,7 @@ pub(crate) fn screen_thread_main( ScreenInstruction::NewPane(pid, client_or_tab_index) => { match client_or_tab_index { ClientOrTabIndex::ClientId(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .new_pane(pid, Some(client_id))); }, ClientOrTabIndex::TabIndex(tab_index) => { @@ -986,35 +1085,49 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .toggle_pane_embed_or_floating(client_id)); screen.unblock_input(); screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins screen.render(); }, ScreenInstruction::ToggleFloatingPanes(client_id, default_shell) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .toggle_floating_panes(client_id, default_shell)); screen.unblock_input(); screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins screen.render(); }, + ScreenInstruction::ShowFloatingPanes(client_id) => { + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, _client_id: ClientId| tab + .show_floating_panes()); + screen.unblock_input(); + screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins + screen.render(); + }, + ScreenInstruction::HideFloatingPanes(client_id) => { + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, _client_id: ClientId| tab + .hide_floating_panes()); + screen.unblock_input(); + screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins + screen.render(); + }, ScreenInstruction::HorizontalSplit(pid, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .horizontal_split(pid, client_id)); screen.unblock_input(); screen.update_tabs(); screen.render(); }, ScreenInstruction::VerticalSplit(pid, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .vertical_split(pid, client_id)); screen.unblock_input(); screen.update_tabs(); screen.render(); }, ScreenInstruction::WriteCharacter(bytes, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| { + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| { match tab.is_sync_panes_active() { true => tab.write_to_terminals_on_current_tab(bytes), false => tab.write_to_active_terminal(bytes, client_id), @@ -1022,51 +1135,51 @@ pub(crate) fn screen_thread_main( }); }, ScreenInstruction::ResizeLeft(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .resize_left(client_id)); screen.render(); }, ScreenInstruction::ResizeRight(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .resize_right(client_id)); screen.render(); }, ScreenInstruction::ResizeDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .resize_down(client_id)); screen.render(); }, ScreenInstruction::ResizeUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab.resize_up(client_id)); + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_up(client_id)); screen.render(); }, ScreenInstruction::ResizeIncrease(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .resize_increase(client_id)); screen.render(); }, ScreenInstruction::ResizeDecrease(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .resize_decrease(client_id)); screen.render(); }, ScreenInstruction::SwitchFocus(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .focus_next_pane(client_id)); screen.render(); }, ScreenInstruction::FocusNextPane(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .focus_next_pane(client_id)); screen.render(); }, ScreenInstruction::FocusPreviousPane(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .focus_previous_pane(client_id)); screen.render(); }, ScreenInstruction::MoveFocusLeft(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_focus_left(client_id)); screen.render(); }, @@ -1076,12 +1189,12 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::MoveFocusDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_focus_down(client_id)); screen.render(); }, ScreenInstruction::MoveFocusRight(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_focus_right(client_id)); screen.render(); }, @@ -1091,99 +1204,100 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::MoveFocusUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_focus_up(client_id)); screen.render(); }, ScreenInstruction::DumpScreen(file, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .dump_active_terminal_screen(Some(file.to_string()), client_id)); screen.render(); }, ScreenInstruction::EditScrollback(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .edit_scrollback(client_id)); screen.render(); }, ScreenInstruction::ScrollUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_up(client_id)); screen.render(); }, ScreenInstruction::MovePane(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_active_pane(client_id)); screen.render(); }, ScreenInstruction::MovePaneDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_active_pane_down(client_id)); screen.render(); }, ScreenInstruction::MovePaneUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_active_pane_up(client_id)); screen.render(); }, ScreenInstruction::MovePaneRight(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_active_pane_right(client_id)); screen.render(); }, ScreenInstruction::MovePaneLeft(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .move_active_pane_left(client_id)); screen.render(); }, ScreenInstruction::ScrollUpAt(point, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .handle_scrollwheel_up(&point, 3, client_id)); screen.render(); }, ScreenInstruction::ScrollDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_down(client_id)); screen.render(); }, ScreenInstruction::ScrollDownAt(point, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .handle_scrollwheel_down(&point, 3, client_id)); screen.render(); }, ScreenInstruction::ScrollToBottom(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_to_bottom(client_id)); screen.render(); }, ScreenInstruction::PageScrollUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_up_page(client_id)); screen.render(); }, ScreenInstruction::PageScrollDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_down_page(client_id)); screen.render(); }, ScreenInstruction::HalfPageScrollUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_up_half_page(client_id)); screen.render(); }, ScreenInstruction::HalfPageScrollDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .scroll_active_terminal_down_half_page(client_id)); screen.render(); }, ScreenInstruction::ClearScroll(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .clear_active_terminal_scroll(client_id)); screen.render(); }, ScreenInstruction::CloseFocusedPane(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .close_focused_pane(client_id)); - screen.update_tabs(); // update_tabs eventually calls render through the plugin thread + screen.update_tabs(); + screen.render(); }, ScreenInstruction::SetSelectable(id, selectable, tab_index) => { screen.get_indexed_tab_mut(tab_index).map_or_else( @@ -1216,17 +1330,17 @@ pub(crate) fn screen_thread_main( screen.update_tabs(); }, ScreenInstruction::UpdatePaneName(c, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .update_active_pane_name(c, client_id)); screen.render(); }, ScreenInstruction::UndoRenamePane(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .undo_active_rename_pane(client_id)); screen.render(); }, ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .toggle_active_pane_fullscreen(client_id)); screen.update_tabs(); screen.render(); @@ -1259,9 +1373,14 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::GoToTab(tab_index, client_id) => { - if let Some(client_id) = - client_id.or_else(|| screen.active_tab_indices.keys().next().copied()) - { + let client_id = if client_id.is_none() { + None + } else if screen.active_tab_indices.contains_key(&client_id.unwrap()) { + client_id + } else { + screen.active_tab_indices.keys().next().copied() + }; + if let Some(client_id) = client_id { screen.go_to_tab(tab_index as usize, client_id); screen.unblock_input(); screen.render(); @@ -1295,8 +1414,12 @@ pub(crate) fn screen_thread_main( screen.change_mode(mode_info, client_id); screen.render(); }, + ScreenInstruction::ChangeModeForAllClients(mode_info) => { + screen.change_mode_for_all_clients(mode_info); + screen.render(); + }, ScreenInstruction::ToggleActiveSyncTab(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, _client_id: ClientId| tab .toggle_sync_panes_is_active()); screen.update_tabs(); screen.render(); @@ -1398,31 +1521,31 @@ pub(crate) fn screen_thread_main( screen.unblock_input(); }, ScreenInstruction::UpdateSearch(c, client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .update_search_term(c, client_id)); screen.render(); }, ScreenInstruction::SearchDown(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .search_down(client_id)); screen.render(); }, ScreenInstruction::SearchUp(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab.search_up(client_id)); + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab.search_up(client_id)); screen.render(); }, ScreenInstruction::SearchToggleCaseSensitivity(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .toggle_search_case_sensitivity(client_id)); screen.render(); }, ScreenInstruction::SearchToggleWrap(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .toggle_search_wrap(client_id)); screen.render(); }, ScreenInstruction::SearchToggleWholeWord(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab + active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab .toggle_search_whole_words(client_id)); screen.render(); }, diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 6a1f27a937..beaafd900c 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -734,6 +734,14 @@ impl Tab { } self.set_force_render(); } + pub fn show_floating_panes(&mut self) { + self.floating_panes.toggle_show_panes(true); + self.set_force_render(); + } + pub fn hide_floating_panes(&mut self) { + self.floating_panes.toggle_show_panes(false); + self.set_force_render(); + } pub fn new_pane(&mut self, pid: PaneId, client_id: Option) { self.close_down_to_max_terminals(); if self.floating_panes.panes_are_visible() { @@ -2034,7 +2042,6 @@ impl Tab { position_on_screen: &Position, client_id: ClientId, ) -> bool { - println!("mouse hold middle"); // return value indicates whether we should trigger a render // determine if event is repeated to enable smooth scrolling let is_repeated = if let Some(last_position) = self.last_mouse_hold_position { @@ -2042,13 +2049,11 @@ impl Tab { } else { false }; - println!("is repeated: {:?}", is_repeated); self.last_mouse_hold_position = Some(*position_on_screen); let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); if let Some(active_pane) = active_pane { - println!("can have active pane"); let mut relative_position = active_pane.relative_position(position_on_screen); if !is_repeated { relative_position.change_column( diff --git a/zellij-server/src/thread_bus.rs b/zellij-server/src/thread_bus.rs index 1bb00c09f4..21ddecfea4 100644 --- a/zellij-server/src/thread_bus.rs +++ b/zellij-server/src/thread_bus.rs @@ -147,6 +147,12 @@ impl Bus { } } #[allow(unused)] + pub fn should_silently_fail(mut self) -> Self { + // this is mostly used for the tests + self.senders.should_silently_fail = true; + self + } + #[allow(unused)] pub fn empty() -> Self { // this is mostly used for the tests Bus { diff --git a/zellij-server/src/unit/fixtures/layout-with-three-panes.kdl b/zellij-server/src/unit/fixtures/layout-with-three-panes.kdl new file mode 100644 index 0000000000..ea1f01e754 --- /dev/null +++ b/zellij-server/src/unit/fixtures/layout-with-three-panes.kdl @@ -0,0 +1,5 @@ +layout { + pane + pane + pane +} diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 15526ac8e2..765af7a4e5 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1,29 +1,111 @@ -use super::{CopyOptions, Screen, ScreenInstruction}; +use super::{CopyOptions, Screen, ScreenInstruction, screen_thread_main}; use crate::panes::PaneId; +use insta::assert_snapshot; use crate::{ + route::route_action, os_input_output::{AsyncReader, Pid, ServerOsApi}, thread_bus::Bus, + channels::SenderWithContext, ClientId, + ServerInstruction, SessionMetaData, ThreadSenders, }; use std::path::PathBuf; +use zellij_utils::input::options::Options; +use zellij_utils::input::actions::{Action, ResizeDirection, Direction}; use zellij_utils::input::command::TerminalAction; -use zellij_utils::input::layout::PaneLayout; +use zellij_utils::input::layout::{PaneLayout, SplitDirection}; +use zellij_utils::cli::CliAction; use zellij_utils::ipc::IpcReceiverWithContext; +use zellij_utils::errors::ErrorContext; use zellij_utils::pane_size::{Size, SizeInPixels}; +use crate::pty_writer::PtyWriteInstruction; use std::os::unix::io::RawFd; +use std::sync::{Arc, Mutex}; -use zellij_utils::ipc::{ClientAttributes, PixelDimensions}; +use zellij_utils::ipc::PixelDimensions; use zellij_utils::nix; - +use crate::{ + pty::PtyInstruction, + wasm_vm::PluginInstruction, +}; use zellij_utils::{ - data::{ModeInfo, Palette}, + channels::{self, ChannelWithContext, Receiver}, + data::{ModeInfo, Palette, PluginCapabilities, InputMode}, interprocess::local_socket::LocalSocketStream, - ipc::{ClientToServerMsg, ServerToClientMsg}, + ipc::{ClientToServerMsg, ServerToClientMsg, ClientAttributes}, }; -#[derive(Clone)] -struct FakeInputOutput {} +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; +use crate::panes::sixel::SixelImageStore; +use crate::panes::grid::Grid; +use crate::panes::link_handler::LinkHandler; +use zellij_utils::vte; + +// TODO: deduplicate with identical function in tab_integration_tests +fn take_snapshot_and_cursor_coordinates(ansi_instructions: &str, rows: usize, columns: usize, palette: Palette) -> (Option<(usize, usize)>, String) { + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let character_cell_size = Rc::new(RefCell::new(Some(SizeInPixels { + width: 8, + height: 21, + }))); + let mut grid = Grid::new( + rows, + columns, + Rc::new(RefCell::new(palette)), + terminal_emulator_color_codes, + Rc::new(RefCell::new(LinkHandler::new())), + character_cell_size, + sixel_image_store, + ); + let mut vte_parser = vte::Parser::new(); + for &byte in ansi_instructions.as_bytes() { + vte_parser.advance(&mut grid, byte); + } + (grid.cursor_coordinates(), format!("{:?}", grid)) +} + +fn take_snapshots_and_cursor_coordinates_from_render_events<'a>(all_events: impl Iterator, screen_size: Size) -> Vec<(Option<(usize, usize)>, String)> { + let snapshots: Vec<(Option<(usize, usize)>, String)> = all_events.filter_map(|server_instruction| { + match server_instruction { + ServerInstruction::Render(output) => { + if let Some(output) = output { + // note this only takes a snapshot of the first client! + let raw_snapshot = output.get(&1).unwrap(); + let snapshot = take_snapshot_and_cursor_coordinates( + raw_snapshot, + screen_size.rows, + screen_size.cols, + Palette::default(), + ); + Some(snapshot) + } else { + None + } + }, + _ => None + } + }).collect(); + snapshots +} + +fn send_cli_action_to_server(session_metadata: &SessionMetaData, cli_action: CliAction, mock_screen: &mut MockScreen, client_id: ClientId) { + let os_input = Box::new(mock_screen.os_input.clone()); + let to_server = mock_screen.to_server.clone(); + let actions = Action::actions_from_cli(cli_action).unwrap(); + for action in actions { + route_action(action, &session_metadata, &*os_input, &to_server.clone(), client_id); + } +} + +#[derive(Clone, Default)] +struct FakeInputOutput { + fake_filesystem: Arc>>, + server_to_client_messages: Arc>>>, +} impl ServerOsApi for FakeInputOutput { fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) { @@ -60,10 +142,11 @@ impl ServerOsApi for FakeInputOutput { } fn send_to_client( &self, - _client_id: ClientId, - _msg: ServerToClientMsg, + client_id: ClientId, + msg: ServerToClientMsg, ) -> Result<(), &'static str> { - unimplemented!() + self.server_to_client_messages.lock().unwrap().entry(client_id).or_insert_with(Vec::new).push(msg); + Ok(()) } fn new_client( &mut self, @@ -81,14 +164,16 @@ impl ServerOsApi for FakeInputOutput { fn get_cwd(&self, _pid: Pid) -> Option { unimplemented!() } - fn write_to_file(&mut self, _: String, _: Option) { - unimplemented!() + fn write_to_file(&mut self, contents: String, filename: Option) { + if let Some(filename) = filename { + self.fake_filesystem.lock().unwrap().insert(filename, contents); + } } } fn create_new_screen(size: Size) -> Screen { let mut bus: Bus = Bus::empty(); - let fake_os_input = FakeInputOutput {}; + let fake_os_input = FakeInputOutput::default(); bus.os_input = Some(Box::new(fake_os_input)); let client_attributes = ClientAttributes { size, @@ -111,6 +196,175 @@ fn create_new_screen(size: Size) -> Screen { ) } +struct MockScreen { + pub main_client_id: u16, + pub pty_receiver: Option>, + pub pty_writer_receiver: Option>, + pub screen_receiver: Option>, + pub server_receiver: Option>, + pub plugin_receiver: Option>, + pub to_screen: SenderWithContext, + pub to_pty: SenderWithContext, + pub to_plugin: SenderWithContext, + pub to_server: SenderWithContext, + pub to_pty_writer: SenderWithContext, + pub os_input: FakeInputOutput, + pub client_attributes: ClientAttributes, + pub config_options: Options, + pub session_metadata: SessionMetaData, +} + +impl MockScreen { + pub fn run(&mut self, initial_layout: Option) -> std::thread::JoinHandle<()> { + let config_options = self.config_options.clone(); + let client_attributes = self.client_attributes.clone(); + let screen_bus = Bus::new( + vec![self.screen_receiver.take().unwrap()], + None, + Some(&self.to_pty.clone()), + Some(&self.to_plugin.clone()), + Some(&self.to_server.clone()), + Some(&self.to_pty_writer.clone()), + Some(Box::new(self.os_input.clone())), + ).should_silently_fail(); + let screen_thread = std::thread::Builder::new() + .name("screen_thread".to_string()) + .spawn(move || screen_thread_main(screen_bus, None, client_attributes, Box::new(config_options))) + .unwrap(); + let pane_layout = initial_layout.unwrap_or_default(); + let pane_count = pane_layout.extract_run_instructions().len(); + let mut pane_ids = vec![]; + for i in 0..pane_count { + pane_ids.push(i as i32); + } + let _ = self.to_screen.send(ScreenInstruction::NewTab(pane_layout, pane_ids, self.main_client_id)); + screen_thread + } + pub fn new_tab(&mut self, tab_layout: PaneLayout) { + let pane_count = tab_layout.extract_run_instructions().len(); + let mut pane_ids = vec![]; + for i in 0..pane_count { + pane_ids.push(i as i32); + } + let _ = self.to_screen.send(ScreenInstruction::NewTab(tab_layout, pane_ids, self.main_client_id)); + } + pub fn teardown(&mut self, threads: Vec>) { + let _ = self.to_pty.send(PtyInstruction::Exit); + let _ = self.to_pty_writer.send(PtyWriteInstruction::Exit); + let _ = self.to_screen.send(ScreenInstruction::Exit); + let _ = self.to_server.send(ServerInstruction::KillSession); + let _ = self.to_plugin.send(PluginInstruction::Exit); + for thread in threads { + let _ = thread.join(); + } + } + pub fn clone_session_metadata(&self) -> SessionMetaData { + // hack that only clones the clonable parts of SessionMetaData + SessionMetaData { + senders: self.session_metadata.senders.clone(), + capabilities: self.session_metadata.capabilities.clone(), + client_attributes: self.session_metadata.client_attributes.clone(), + default_shell: self.session_metadata.default_shell.clone(), + screen_thread: None, + pty_thread: None, + wasm_thread: None, + pty_writer_thread: None, + } + } + +} + +impl MockScreen { + pub fn new(size: Size) -> Self { + let (to_server, server_receiver): ChannelWithContext = channels::bounded(50); + let to_server = SenderWithContext::new(to_server); + + let (to_screen, screen_receiver): ChannelWithContext = channels::unbounded(); + let to_screen = SenderWithContext::new(to_screen); + + let (to_plugin, plugin_receiver): ChannelWithContext = channels::unbounded(); + let to_plugin = SenderWithContext::new(to_plugin); + let (to_pty, pty_receiver): ChannelWithContext = channels::unbounded(); + let to_pty = SenderWithContext::new(to_pty); + + let (to_pty_writer, pty_writer_receiver): ChannelWithContext = + channels::unbounded(); + let to_pty_writer = SenderWithContext::new(to_pty_writer); + + let client_attributes = ClientAttributes { + size, + ..Default::default() + }; + let capabilities = PluginCapabilities { + arrow_fonts: Default::default() + }; + + let session_metadata = SessionMetaData { + senders: ThreadSenders { + to_screen: Some(to_screen.clone()), + to_pty: Some(to_pty.clone()), + to_plugin: Some(to_plugin.clone()), + to_pty_writer: Some(to_pty_writer.clone()), + to_server: Some(to_server.clone()), + should_silently_fail: true, + }, + capabilities, + default_shell: None, + client_attributes: client_attributes.clone(), + screen_thread: None, + pty_thread: None, + wasm_thread: None, + pty_writer_thread: None, + }; + + let os_input = FakeInputOutput::default(); + let config_options = Options::default(); + let main_client_id = 1; + MockScreen { + main_client_id, + pty_receiver: Some(pty_receiver), + pty_writer_receiver: Some(pty_writer_receiver), + screen_receiver: Some(screen_receiver), + server_receiver: Some(server_receiver), + plugin_receiver: Some(plugin_receiver), + to_screen, + to_pty, + to_plugin, + to_server, + to_pty_writer, + os_input, + client_attributes, + config_options, + session_metadata, + } + } +} + +macro_rules! log_actions_in_thread { + ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr ) => { + std::thread::Builder::new() + .name("pty_writer_thread".to_string()) + .spawn({ + let log = $arc_mutex_log.clone(); + move || { + loop { + let (event, _err_ctx) = $receiver.recv().expect("failed to receive event on channel"); + match event { + $exit_event => { + log.lock().unwrap().push(event); + break; + } + _ => { + log.lock().unwrap().push(event); + } + } + } + } + }) + .unwrap() + } +} + fn new_tab(screen: &mut Screen, pid: i32) { let client_id = 1; screen.new_tab( @@ -569,3 +823,1451 @@ fn attach_after_first_tab_closed() { screen.remove_client(1); screen.add_client(1); } + +// Following are tests for sending CLI actions +// these tests are only partially relevant to Screen +// and are included here for two reasons: +// 1. The best way to "integration test" these is combining the "screen_thread_main" and +// "route_action" functions and mocking everything around them +// 2. These inadvertently also test many parts of Screen that are not tested elsewhere + +#[test] +pub fn send_cli_write_chars_action_to_screen() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_writer_receiver = mock_screen.pty_writer_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(None); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_writer_thread = log_actions_in_thread!( + received_pty_instructions, + PtyWriteInstruction::Exit, + pty_writer_receiver + ); + let cli_action = CliAction::WriteChars { chars: "input from the cli".into() }; + send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_writer_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_write_action_to_screen() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_writer_receiver = mock_screen.pty_writer_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(None); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_writer_thread = log_actions_in_thread!( + received_pty_instructions, + PtyWriteInstruction::Exit, + pty_writer_receiver + ); + let cli_action = CliAction::Write { bytes: vec![102, 111, 111] }; + send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_writer_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_resize_action_to_screen() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let pty_writer_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let resize_cli_action = CliAction::Resize { resize_direction: ResizeDirection::Left }; + send_cli_action_to_server(&session_metadata, resize_cli_action, &mut mock_screen, client_id); + mock_screen.teardown(vec![pty_writer_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_focus_next_pane_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let focus_next_pane_action = CliAction::FocusNextPane; + send_cli_action_to_server(&session_metadata, focus_next_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (cursor_coordinates, _snapshot) in snapshots { + // here we assert he cursor_coordinates to let us know if we switched the pane focus + assert_snapshot!(format!("{:?}", cursor_coordinates)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_focus_previous_pane_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let focus_next_pane_action = CliAction::FocusPreviousPane; + send_cli_action_to_server(&session_metadata, focus_next_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (cursor_coordinates, _snapshot) in snapshots { + // here we assert he cursor_coordinates to let us know if we switched the pane focus + assert_snapshot!(format!("{:?}", cursor_coordinates)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_move_focus_pane_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let move_focus_action = CliAction::MoveFocus { direction: Direction::Right }; + send_cli_action_to_server(&session_metadata, move_focus_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (cursor_coordinates, _snapshot) in snapshots { + // here we assert he cursor_coordinates to let us know if we switched the pane focus + assert_snapshot!(format!("{:?}", cursor_coordinates)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_move_focus_or_tab_pane_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let move_focus_action = CliAction::MoveFocusOrTab { direction: Direction::Right }; + send_cli_action_to_server(&session_metadata, move_focus_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (cursor_coordinates, _snapshot) in snapshots { + // here we assert he cursor_coordinates to let us know if we switched the pane focus + assert_snapshot!(format!("{:?}", cursor_coordinates)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_move_pane_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let cli_action = CliAction::MovePane { direction: Direction::Right }; + send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_dump_screen_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let cli_action = CliAction::DumpScreen { path: PathBuf::from("/tmp/foo") }; + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, "fill pane up with something".as_bytes().to_vec())); + send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *mock_screen.os_input.fake_filesystem.lock().unwrap())); +} + +#[test] +pub fn send_cli_edit_scrollback_action() { + let size = Size { + cols: 80, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_action = CliAction::EditScrollback; + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, "fill pane up with something".as_bytes().to_vec())); + send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![pty_thread, screen_thread]); + let dumped_file_name = mock_screen.os_input.fake_filesystem.lock().unwrap().keys().next().unwrap().clone(); + let mut found_instruction = false; + for instruction in received_pty_instructions.lock().unwrap().iter() { + if let PtyInstruction::OpenInPlaceEditor(scrollback_contents_file, terminal_id, client_id) = instruction { + assert_eq!(scrollback_contents_file, &PathBuf::from(&dumped_file_name)); + assert_eq!(terminal_id, &Some(1)); + assert_eq!(client_id, &1); + found_instruction = true; + } + } + assert!(found_instruction); +} + +#[test] +pub fn send_cli_scroll_up_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let cli_action = CliAction::ScrollUp; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + // we send two actions here because only the last line in the pane is empty, so one action + // won't show in a render + send_cli_action_to_server(&session_metadata, cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, cli_action.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_scroll_down_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let scroll_up_cli_action = CliAction::ScrollUp; + let scroll_down_cli_action = CliAction::ScrollDown; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + // scroll up some + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + + // scroll down some + send_cli_action_to_server(&session_metadata, scroll_down_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_down_cli_action.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_scroll_to_bottom_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let scroll_up_cli_action = CliAction::ScrollUp; + let scroll_to_bottom_action = CliAction::ScrollToBottom; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + // scroll up some + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + + // scroll to bottom + send_cli_action_to_server(&session_metadata, scroll_to_bottom_action.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_page_scroll_up_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let page_scroll_up_action = CliAction::PageScrollUp; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + send_cli_action_to_server(&session_metadata, page_scroll_up_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_page_scroll_down_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let page_scroll_up_action = CliAction::PageScrollUp; + let page_scroll_down_action = CliAction::PageScrollDown; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + + // scroll up some + send_cli_action_to_server(&session_metadata, page_scroll_up_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, page_scroll_up_action.clone(), &mut mock_screen, client_id); + + // scroll down + send_cli_action_to_server(&session_metadata, page_scroll_down_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_half_page_scroll_up_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let half_page_scroll_up_action = CliAction::HalfPageScrollUp; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + send_cli_action_to_server(&session_metadata, half_page_scroll_up_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_half_page_scroll_down_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let half_page_scroll_up_action = CliAction::HalfPageScrollUp; + let half_page_scroll_down_action = CliAction::HalfPageScrollDown; + let mut pane_contents = String::new(); + for i in 0..20 { + pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); + } + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + std::thread::sleep(std::time::Duration::from_millis(100)); + + // scroll up some + send_cli_action_to_server(&session_metadata, half_page_scroll_up_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, half_page_scroll_up_action.clone(), &mut mock_screen, client_id); + + // scroll down + send_cli_action_to_server(&session_metadata, half_page_scroll_down_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_toggle_full_screen_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let toggle_full_screen_action = CliAction::ToggleFullscreen; + send_cli_action_to_server(&session_metadata, toggle_full_screen_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_toggle_pane_frames_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let toggle_pane_frames_action = CliAction::TogglePaneFrames; + send_cli_action_to_server(&session_metadata, toggle_pane_frames_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_toggle_active_tab_sync_action() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_writer_receiver = mock_screen.pty_writer_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_writer_thread = log_actions_in_thread!( + received_pty_instructions, + PtyWriteInstruction::Exit, + pty_writer_receiver + ); + let cli_toggle_active_tab_sync_action = CliAction::ToggleActiveSyncTab; + let cli_write_action = CliAction::Write { bytes: vec![102, 111, 111] }; + send_cli_action_to_server(&session_metadata, cli_toggle_active_tab_sync_action, &mut mock_screen, client_id); + send_cli_action_to_server(&session_metadata, cli_write_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_writer_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_new_pane_action_with_default_parameters() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_new_pane_action = CliAction::NewPane { + direction: None, + command: None, + cwd: None, + args: None, + floating: None + }; + send_cli_action_to_server(&session_metadata, cli_new_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_new_pane_action_with_split_direction() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_new_pane_action = CliAction::NewPane { + direction: Some(Direction::Right), + command: None, + cwd: None, + args: None, + floating: None + }; + send_cli_action_to_server(&session_metadata, cli_new_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_new_pane_action_with_command_cwd_and_args() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_new_pane_action = CliAction::NewPane { + direction: Some(Direction::Right), + command: Some("htop".into()), + cwd: Some("/some/folder".into()), + args: Some("-h --something arg".into()), + floating: None + }; + send_cli_action_to_server(&session_metadata, cli_new_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_edit_action_with_default_parameters() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_edit_action = CliAction::Edit { + file: PathBuf::from("/file/to/edit"), + direction: None, + line_number: None, + floating: None + }; + send_cli_action_to_server(&session_metadata, cli_edit_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_edit_action_with_line_number() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_edit_action = CliAction::Edit { + file: PathBuf::from("/file/to/edit"), + direction: None, + line_number: Some(100), + floating: None + }; + send_cli_action_to_server(&session_metadata, cli_edit_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_edit_action_with_split_direction() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let cli_edit_action = CliAction::Edit { + file: PathBuf::from("/file/to/edit"), + direction: Some(Direction::Down), + line_number: None, + floating: None + }; + send_cli_action_to_server(&session_metadata, cli_edit_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_switch_mode_action() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let screen_thread = mock_screen.run(Some(initial_layout)); + let cli_switch_mode = CliAction::SwitchMode { input_mode: InputMode::Locked }; + send_cli_action_to_server(&session_metadata, cli_switch_mode, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be + mock_screen.teardown(vec![screen_thread]); + assert_snapshot!(format!("{:?}", *mock_screen.os_input.server_to_client_messages.lock().unwrap())); +} + +#[test] +pub fn send_cli_toggle_pane_embed_or_float() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let toggle_pane_embed_or_floating = CliAction::TogglePaneEmbedOrFloating; + // first time to float + send_cli_action_to_server(&session_metadata, toggle_pane_embed_or_floating.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + // second time to embed + send_cli_action_to_server(&session_metadata, toggle_pane_embed_or_floating.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_toggle_floating_panes() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let toggle_pane_embed_or_floating = CliAction::TogglePaneEmbedOrFloating; + let toggle_floating_panes = CliAction::ToggleFloatingPanes; + // float the focused pane + send_cli_action_to_server(&session_metadata, toggle_pane_embed_or_floating, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + // toggle floating panes (will hide the floated pane from the previous action) + send_cli_action_to_server(&session_metadata, toggle_floating_panes.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + // toggle floating panes (will show the floated pane) + send_cli_action_to_server(&session_metadata, toggle_floating_panes.clone(), &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_close_pane_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let close_pane_action = CliAction::ClosePane; + send_cli_action_to_server(&session_metadata, close_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_rename_pane_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let rename_pane_action = CliAction::RenamePane { name: "my_new_pane_title".into() }; + send_cli_action_to_server(&session_metadata, rename_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_undo_rename_pane_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_instruction = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let rename_pane_action = CliAction::RenamePane { name: "my_new_pane_title".into() }; + let undo_rename_pane_action = CliAction::UndoRenamePane; + // first rename the pane + send_cli_action_to_server(&session_metadata, rename_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + // then undo the rename, returning to the default name + send_cli_action_to_server(&session_metadata, undo_rename_pane_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_instruction, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_new_tab_action_default_params() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let new_tab_action = CliAction::NewTab { name: None, layout: None }; + send_cli_action_to_server(&session_metadata, new_tab_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_new_tab_action_with_name_and_layout() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_receiver = mock_screen.pty_receiver.take().unwrap(); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::Exit, + pty_receiver + ); + let new_tab_action = CliAction::NewTab { + name: Some("my-awesome-tab-name".into()), + layout: Some(PathBuf::from(format!("{}/src/unit/fixtures/layout-with-three-panes.kdl", env!("CARGO_MANIFEST_DIR")))), + }; + send_cli_action_to_server(&session_metadata, new_tab_action, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![pty_thread, screen_thread]); + assert_snapshot!(format!("{:#?}", *received_pty_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_next_tab_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let goto_next_tab = CliAction::GoToNextTab; + send_cli_action_to_server(&session_metadata, goto_next_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_previous_tab_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let goto_previous_tab = CliAction::GoToPreviousTab; + send_cli_action_to_server(&session_metadata, goto_previous_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_goto_tab_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let goto_tab = CliAction::GoToTab { index: 1 }; + send_cli_action_to_server(&session_metadata, goto_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_close_tab_action() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let close_tab = CliAction::CloseTab; + send_cli_action_to_server(&session_metadata, close_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshot_count = snapshots.len(); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } + assert_snapshot!(format!("{}", snapshot_count)); +} + +#[test] +pub fn send_cli_rename_tab() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_plugin_instructions = Arc::new(Mutex::new(vec![])); + let plugin_receiver = mock_screen.plugin_receiver.take().unwrap(); + let plugin_thread = log_actions_in_thread!( + received_plugin_instructions, + PluginInstruction::Exit, + plugin_receiver + ); + let rename_tab = CliAction::RenameTab { name: "new-tab-name".into() }; + send_cli_action_to_server(&session_metadata, rename_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![plugin_thread, screen_thread]); + assert_snapshot!(format!("{:#?}", *received_plugin_instructions.lock().unwrap())) +} + +#[test] +pub fn send_cli_undo_rename_tab() { + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_plugin_instructions = Arc::new(Mutex::new(vec![])); + let plugin_receiver = mock_screen.plugin_receiver.take().unwrap(); + let plugin_thread = log_actions_in_thread!( + received_plugin_instructions, + PluginInstruction::Exit, + plugin_receiver + ); + let rename_tab = CliAction::RenameTab { name: "new-tab-name".into() }; + let undo_rename_tab = CliAction::UndoRenameTab; + // first rename the tab + send_cli_action_to_server(&session_metadata, rename_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + // then undo the tab rename to go back to the default name + send_cli_action_to_server(&session_metadata, undo_rename_tab, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![plugin_thread, screen_thread]); + assert_snapshot!(format!("{:#?}", *received_plugin_instructions.lock().unwrap())) +} + +#[test] +pub fn send_cli_update_search_term() { + // the output (snapshot) in this test is unfortuantely not very Human readable + // this is because the search term only updates the styling, and we don't have a good way to + // snapshot styling (we usually strip them) + // + // if this test fails, it means something it wrong with searching through the CLI + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let update_search_term = CliAction::SearchInput { input: "my-search-term".into() }; + send_cli_action_to_server(&session_metadata, update_search_term, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_server_instructions.lock().unwrap())); +} + +#[test] +pub fn send_cli_go_to_next_and_previous_search_occurrence() { + // the output (snapshot) in this test is unfortuantely not very Human readable + // this is because the search term only updates the styling, and we don't have a good way to + // snapshot styling (we usually strip them) + // + // if this test fails, it means something it wrong with searching through the CLI + // specifically going to the previous/next search occurrence + let size = Size { + cols: 80, + rows: 10, + }; + let client_id = 10; // fake client id should not appear in the screen's state + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut second_tab_layout = PaneLayout::default(); + second_tab_layout.children_split_direction = SplitDirection::Horizontal; + second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.new_tab(second_tab_layout); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(initial_layout)); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let update_search_term = CliAction::SearchInput { input: "my-search-term".into() }; + let go_to_previous_search = CliAction::SearchPrevious; + let go_to_next_search = CliAction::SearchNext; + send_cli_action_to_server(&session_metadata, update_search_term, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + send_cli_action_to_server(&session_metadata, go_to_previous_search, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + send_cli_action_to_server(&session_metadata, go_to_next_search, &mut mock_screen, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + assert_snapshot!(format!("{:?}", *received_server_instructions.lock().unwrap())); +} diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-2.snap new file mode 100644 index 0000000000..2df4565e40 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1805 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-3.snap new file mode 100644 index 0000000000..c8e5e9a97e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1805 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-4.snap new file mode 100644 index 0000000000..163f7b356c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1850 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action.snap new file mode 100644 index 0000000000..6c5fd6ab06 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1805 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-2.snap new file mode 100644 index 0000000000..eea7656b1a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2118 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-3.snap new file mode 100644 index 0000000000..b4240eb037 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2118 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-4.snap new file mode 100644 index 0000000000..eea7656b1a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2118 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-5.snap new file mode 100644 index 0000000000..d4fce1119d --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2118 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-6.snap new file mode 100644 index 0000000000..eea7656b1a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-6.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2118 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-7.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-7.snap new file mode 100644 index 0000000000..f6af649c85 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-7.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2120 +expression: "format!(\"{}\", snapshot_count)" +--- +6 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action.snap new file mode 100644 index 0000000000..d4fce1119d --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2118 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_dump_screen_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_dump_screen_action.snap new file mode 100644 index 0000000000..6eb6814d90 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_dump_screen_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1074 +expression: "format!(\"{:?}\", * mock_screen.os_input.fake_filesystem.lock().unwrap())" +--- +{"/tmp/foo": "fill pane up with something"} diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap new file mode 100644 index 0000000000..776d37ff90 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1618 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminal(Some(OpenFile("/file/to/edit", None)), ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap new file mode 100644 index 0000000000..9d4ecb19c8 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1650 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminal(Some(OpenFile("/file/to/edit", Some(100))), ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap new file mode 100644 index 0000000000..1d793cf663 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1682 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminalHorizontally(Some(OpenFile("/file/to/edit", None)), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-2.snap new file mode 100644 index 0000000000..7446079629 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-2.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 939 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-3.snap new file mode 100644 index 0000000000..f0d453ebee --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-3.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 939 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((41, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-4.snap new file mode 100644 index 0000000000..ae95b51be9 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 937 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action.snap new file mode 100644 index 0000000000..7446079629 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 939 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-2.snap new file mode 100644 index 0000000000..c886124027 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-2.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 968 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-3.snap new file mode 100644 index 0000000000..89f17aa722 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-3.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 968 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((41, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-4.snap new file mode 100644 index 0000000000..1189125a54 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 970 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action.snap new file mode 100644 index 0000000000..c886124027 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 968 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap new file mode 100644 index 0000000000..7d750dbb51 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2269 +expression: "format!(\"{:?}\", * received_server_instructions.lock().unwrap())" +--- +[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-2.snap new file mode 100644 index 0000000000..6eb1076084 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2037 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-3.snap new file mode 100644 index 0000000000..a2f3676722 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2037 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-4.snap new file mode 100644 index 0000000000..6eb1076084 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2037 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-5.snap new file mode 100644 index 0000000000..1688290e70 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2037 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-6.snap new file mode 100644 index 0000000000..6eb1076084 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-6.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2037 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-7.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-7.snap new file mode 100644 index 0000000000..e8b27b15c8 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-7.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2040 +expression: "format!(\"{}\", snapshot_count)" +--- +6 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action.snap new file mode 100644 index 0000000000..1688290e70 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2037 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-2.snap new file mode 100644 index 0000000000..0f0a9e250b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1397 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-3.snap new file mode 100644 index 0000000000..884173f40b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1397 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 4/13 ┐ +01 (C): │fill pane up with something 9 │ +02 (C): │fill pane up with something 10 │ +03 (C): │fill pane up with something 11 │ +04 (C): │fill pane up with something 12 │ +05 (C): │fill pane up with something 13 │ +06 (C): │fill pane up with something 14 │ +07 (C): │fill pane up with something 15 │ +08 (C): │fill pane up with something 16 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-4.snap new file mode 100644 index 0000000000..8118d806e3 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1397 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 8/13 ┐ +01 (C): │fill pane up with something 5 │ +02 (C): │fill pane up with something 6 │ +03 (C): │fill pane up with something 7 │ +04 (C): │fill pane up with something 8 │ +05 (C): │fill pane up with something 9 │ +06 (C): │fill pane up with something 10 │ +07 (C): │fill pane up with something 11 │ +08 (C): │fill pane up with something 12 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-5.snap new file mode 100644 index 0000000000..884173f40b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1397 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 4/13 ┐ +01 (C): │fill pane up with something 9 │ +02 (C): │fill pane up with something 10 │ +03 (C): │fill pane up with something 11 │ +04 (C): │fill pane up with something 12 │ +05 (C): │fill pane up with something 13 │ +06 (C): │fill pane up with something 14 │ +07 (C): │fill pane up with something 15 │ +08 (C): │fill pane up with something 16 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-6.snap new file mode 100644 index 0000000000..63ab9f8d71 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-6.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1434 +expression: "format!(\"{}\", snapshot_count)" +--- +5 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action.snap new file mode 100644 index 0000000000..d03e1da452 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1397 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-2.snap new file mode 100644 index 0000000000..4fdd411bc1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1354 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-3.snap new file mode 100644 index 0000000000..f0b88e27c4 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1354 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 4/13 ┐ +01 (C): │fill pane up with something 9 │ +02 (C): │fill pane up with something 10 │ +03 (C): │fill pane up with something 11 │ +04 (C): │fill pane up with something 12 │ +05 (C): │fill pane up with something 13 │ +06 (C): │fill pane up with something 14 │ +07 (C): │fill pane up with something 15 │ +08 (C): │fill pane up with something 16 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-4.snap new file mode 100644 index 0000000000..8bc6c44b0e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1389 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action.snap new file mode 100644 index 0000000000..d38454b455 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1354 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-2.snap new file mode 100644 index 0000000000..7a6fd48a20 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-2.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1030 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-3.snap new file mode 100644 index 0000000000..400b84d73f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-3.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1030 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((41, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-4.snap new file mode 100644 index 0000000000..97cb88dbb3 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1036 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action.snap new file mode 100644 index 0000000000..7a6fd48a20 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1030 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-2.snap new file mode 100644 index 0000000000..208d683f33 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-2.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 999 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-3.snap new file mode 100644 index 0000000000..407ab4bb6e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-3.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 999 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((41, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-4.snap new file mode 100644 index 0000000000..eacdaba2e8 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1003 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action.snap new file mode 100644 index 0000000000..208d683f33 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 999 +expression: "format!(\"{:?}\", cursor_coordinates)" +--- +Some((1, 1)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-2.snap new file mode 100644 index 0000000000..14158fbda2 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-2.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1060 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): +10 (C): +11 (C): +12 (C): +13 (C): +14 (C): +15 (C): +16 (C): +17 (C): +18 (C): +19 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-3.snap new file mode 100644 index 0000000000..d4fc90dd2d --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-3.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1060 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────┐┌ Pane #1 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-4.snap new file mode 100644 index 0000000000..4c454d89f1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1068 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action.snap new file mode 100644 index 0000000000..1c054e3a01 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1061 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap new file mode 100644 index 0000000000..4f577fef16 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1586 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: ["-h", "--something", "arg"], cwd: Some("/some/folder") })), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap new file mode 100644 index 0000000000..bdb1bd7e95 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1520 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminal(None, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap new file mode 100644 index 0000000000..6e7c599d7b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1553 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminalVertically(None, 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap new file mode 100644 index 0000000000..53a024268a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1898 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[NewTab(None, None, None, 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap new file mode 100644 index 0000000000..f39faf4d52 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap @@ -0,0 +1,74 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1928 +expression: "format!(\"{:#?}\", * received_pty_instructions.lock().unwrap())" +--- +[ + NewTab( + None, + Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), + Some( + "my-awesome-tab-name", + ), + 10, + ), + UpdateActivePane( + Some( + Terminal( + 0, + ), + ), + 1, + ), + UpdateActivePane( + Some( + Terminal( + 0, + ), + ), + 1, + ), + Exit, +] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-2.snap new file mode 100644 index 0000000000..f44abd51b1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1969 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-3.snap new file mode 100644 index 0000000000..6c8d76298f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1969 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-4.snap new file mode 100644 index 0000000000..f44abd51b1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1969 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-5.snap new file mode 100644 index 0000000000..fdac85da8d --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1969 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-6.snap new file mode 100644 index 0000000000..f44abd51b1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-6.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1969 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-7.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-7.snap new file mode 100644 index 0000000000..8e7e4eaec3 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-7.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2012 +expression: "format!(\"{}\", snapshot_count)" +--- +6 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action.snap new file mode 100644 index 0000000000..fdac85da8d --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1969 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-2.snap new file mode 100644 index 0000000000..dd4795d31f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1318 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-3.snap new file mode 100644 index 0000000000..38ae66192c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1318 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 9/13 ┐ +01 (C): │fill pane up with something 4 │ +02 (C): │fill pane up with something 5 │ +03 (C): │fill pane up with something 6 │ +04 (C): │fill pane up with something 7 │ +05 (C): │fill pane up with something 8 │ +06 (C): │fill pane up with something 9 │ +07 (C): │fill pane up with something 10 │ +08 (C): │fill pane up with something 11 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-4.snap new file mode 100644 index 0000000000..73c5898962 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1318 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ───────────── SCROLL: 13/13 ┐ +01 (C): │fill pane up with something 0 │ +02 (C): │fill pane up with something 1 │ +03 (C): │fill pane up with something 2 │ +04 (C): │fill pane up with something 3 │ +05 (C): │fill pane up with something 4 │ +06 (C): │fill pane up with something 5 │ +07 (C): │fill pane up with something 6 │ +08 (C): │fill pane up with something 7 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-5.snap new file mode 100644 index 0000000000..5de916b800 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1318 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 5/13 ┐ +01 (C): │fill pane up with something 8 │ +02 (C): │fill pane up with something 9 │ +03 (C): │fill pane up with something 10 │ +04 (C): │fill pane up with something 11 │ +05 (C): │fill pane up with something 12 │ +06 (C): │fill pane up with something 13 │ +07 (C): │fill pane up with something 14 │ +08 (C): │fill pane up with something 15 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-6.snap new file mode 100644 index 0000000000..fc4136a68b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-6.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1351 +expression: "format!(\"{}\", snapshot_count)" +--- +5 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action.snap new file mode 100644 index 0000000000..19f6d030cf --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1318 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-2.snap new file mode 100644 index 0000000000..15f71daf2e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1275 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-3.snap new file mode 100644 index 0000000000..481e5b2381 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1275 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 9/13 ┐ +01 (C): │fill pane up with something 4 │ +02 (C): │fill pane up with something 5 │ +03 (C): │fill pane up with something 6 │ +04 (C): │fill pane up with something 7 │ +05 (C): │fill pane up with something 8 │ +06 (C): │fill pane up with something 9 │ +07 (C): │fill pane up with something 10 │ +08 (C): │fill pane up with something 11 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-4.snap new file mode 100644 index 0000000000..328d4b3a46 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1306 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action.snap new file mode 100644 index 0000000000..a59f686903 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1275 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-2.snap new file mode 100644 index 0000000000..412016df8a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2003 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-3.snap new file mode 100644 index 0000000000..e2ff3ec68b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2003 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-4.snap new file mode 100644 index 0000000000..412016df8a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2003 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-5.snap new file mode 100644 index 0000000000..b0d4263457 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2003 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-6.snap new file mode 100644 index 0000000000..412016df8a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-6.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2003 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-7.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-7.snap new file mode 100644 index 0000000000..648f833d67 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-7.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2048 +expression: "format!(\"{}\", snapshot_count)" +--- +6 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action.snap new file mode 100644 index 0000000000..b0d4263457 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2003 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): └──────────────────────────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-2.snap new file mode 100644 index 0000000000..91d02a51d3 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1835 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-3.snap new file mode 100644 index 0000000000..da90407dde --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1835 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ my_new_pane_title ───────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-4.snap new file mode 100644 index 0000000000..4279479061 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1882 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action.snap new file mode 100644 index 0000000000..24b8735aef --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1835 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap new file mode 100644 index 0000000000..94dc4eb2d7 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap @@ -0,0 +1,436 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2154 +expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" +--- +[ + Update( + None, + Some( + 10, + ), + InputReceived, + ), + Update( + None, + Some( + 10, + ), + InputReceived, + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "Tab #2", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "new-tab-name", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Exit, +] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-2.snap new file mode 100644 index 0000000000..da2e2a7b7a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-2.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 924 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): +10 (C): +11 (C): +12 (C): +13 (C): +14 (C): +15 (C): +16 (C): +17 (C): +18 (C): +19 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-3.snap new file mode 100644 index 0000000000..4325fbb488 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-3.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 924 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────┐┌ Pane #2 ─────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └──────────────────────────────────┘└──────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen.snap new file mode 100644 index 0000000000..3b34c47dad --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 904 +expression: "format!(\"{}\", snapshot_count)" +--- +0 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-2.snap new file mode 100644 index 0000000000..4d0118e137 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-3.snap new file mode 100644 index 0000000000..ce17e6181f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 1/13 ┐ +01 (C): │fill pane up with something 12 │ +02 (C): │fill pane up with something 13 │ +03 (C): │fill pane up with something 14 │ +04 (C): │fill pane up with something 15 │ +05 (C): │fill pane up with something 16 │ +06 (C): │fill pane up with something 17 │ +07 (C): │fill pane up with something 18 │ +08 (C): │fill pane up with something 19 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-4.snap new file mode 100644 index 0000000000..eea8c7f4ec --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 2/13 ┐ +01 (C): │fill pane up with something 11 │ +02 (C): │fill pane up with something 12 │ +03 (C): │fill pane up with something 13 │ +04 (C): │fill pane up with something 14 │ +05 (C): │fill pane up with something 15 │ +06 (C): │fill pane up with something 16 │ +07 (C): │fill pane up with something 17 │ +08 (C): │fill pane up with something 18 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-5.snap new file mode 100644 index 0000000000..e2c021ed60 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 3/13 ┐ +01 (C): │fill pane up with something 10 │ +02 (C): │fill pane up with something 11 │ +03 (C): │fill pane up with something 12 │ +04 (C): │fill pane up with something 13 │ +05 (C): │fill pane up with something 14 │ +06 (C): │fill pane up with something 15 │ +07 (C): │fill pane up with something 16 │ +08 (C): │fill pane up with something 17 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-6.snap new file mode 100644 index 0000000000..9765846dcd --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-6.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 4/13 ┐ +01 (C): │fill pane up with something 9 │ +02 (C): │fill pane up with something 10 │ +03 (C): │fill pane up with something 11 │ +04 (C): │fill pane up with something 12 │ +05 (C): │fill pane up with something 13 │ +06 (C): │fill pane up with something 14 │ +07 (C): │fill pane up with something 15 │ +08 (C): │fill pane up with something 16 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-7.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-7.snap new file mode 100644 index 0000000000..e2c021ed60 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-7.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 3/13 ┐ +01 (C): │fill pane up with something 10 │ +02 (C): │fill pane up with something 11 │ +03 (C): │fill pane up with something 12 │ +04 (C): │fill pane up with something 13 │ +05 (C): │fill pane up with something 14 │ +06 (C): │fill pane up with something 15 │ +07 (C): │fill pane up with something 16 │ +08 (C): │fill pane up with something 17 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-8.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-8.snap new file mode 100644 index 0000000000..eea8c7f4ec --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-8.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 2/13 ┐ +01 (C): │fill pane up with something 11 │ +02 (C): │fill pane up with something 12 │ +03 (C): │fill pane up with something 13 │ +04 (C): │fill pane up with something 14 │ +05 (C): │fill pane up with something 15 │ +06 (C): │fill pane up with something 16 │ +07 (C): │fill pane up with something 17 │ +08 (C): │fill pane up with something 18 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-9.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-9.snap new file mode 100644 index 0000000000..47fdf2d752 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-9.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1222 +expression: "format!(\"{}\", snapshot_count)" +--- +8 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action.snap new file mode 100644 index 0000000000..798b472000 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1193 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-2.snap new file mode 100644 index 0000000000..fd5c4f899b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-3.snap new file mode 100644 index 0000000000..7a2e2371a2 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 1/13 ┐ +01 (C): │fill pane up with something 12 │ +02 (C): │fill pane up with something 13 │ +03 (C): │fill pane up with something 14 │ +04 (C): │fill pane up with something 15 │ +05 (C): │fill pane up with something 16 │ +06 (C): │fill pane up with something 17 │ +07 (C): │fill pane up with something 18 │ +08 (C): │fill pane up with something 19 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-4.snap new file mode 100644 index 0000000000..8e0733960b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 2/13 ┐ +01 (C): │fill pane up with something 11 │ +02 (C): │fill pane up with something 12 │ +03 (C): │fill pane up with something 13 │ +04 (C): │fill pane up with something 14 │ +05 (C): │fill pane up with something 15 │ +06 (C): │fill pane up with something 16 │ +07 (C): │fill pane up with something 17 │ +08 (C): │fill pane up with something 18 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-5.snap new file mode 100644 index 0000000000..657ed3e84e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 3/13 ┐ +01 (C): │fill pane up with something 10 │ +02 (C): │fill pane up with something 11 │ +03 (C): │fill pane up with something 12 │ +04 (C): │fill pane up with something 13 │ +05 (C): │fill pane up with something 14 │ +06 (C): │fill pane up with something 15 │ +07 (C): │fill pane up with something 16 │ +08 (C): │fill pane up with something 17 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-6.snap new file mode 100644 index 0000000000..60f03824ee --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-6.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 4/13 ┐ +01 (C): │fill pane up with something 9 │ +02 (C): │fill pane up with something 10 │ +03 (C): │fill pane up with something 11 │ +04 (C): │fill pane up with something 12 │ +05 (C): │fill pane up with something 13 │ +06 (C): │fill pane up with something 14 │ +07 (C): │fill pane up with something 15 │ +08 (C): │fill pane up with something 16 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-7.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-7.snap new file mode 100644 index 0000000000..92ac7a86d1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-7.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 0/13 ┐ +01 (C): │fill pane up with something 13 │ +02 (C): │fill pane up with something 14 │ +03 (C): │fill pane up with something 15 │ +04 (C): │fill pane up with something 16 │ +05 (C): │fill pane up with something 17 │ +06 (C): │fill pane up with something 18 │ +07 (C): │fill pane up with something 19 │ +08 (C): │ │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-8.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-8.snap new file mode 100644 index 0000000000..9216314269 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-8.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1268 +expression: "format!(\"{}\", snapshot_count)" +--- +7 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action.snap new file mode 100644 index 0000000000..98e19e6783 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1239 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-2.snap new file mode 100644 index 0000000000..cd8a306170 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1147 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-3.snap new file mode 100644 index 0000000000..d61adfbcdc --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1147 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 1/13 ┐ +01 (C): │fill pane up with something 12 │ +02 (C): │fill pane up with something 13 │ +03 (C): │fill pane up with something 14 │ +04 (C): │fill pane up with something 15 │ +05 (C): │fill pane up with something 16 │ +06 (C): │fill pane up with something 17 │ +07 (C): │fill pane up with something 18 │ +08 (C): │fill pane up with something 19 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-4.snap new file mode 100644 index 0000000000..a2ef5fa2a2 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1149 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ────────────── SCROLL: 2/13 ┐ +01 (C): │fill pane up with something 11 │ +02 (C): │fill pane up with something 12 │ +03 (C): │fill pane up with something 13 │ +04 (C): │fill pane up with something 14 │ +05 (C): │fill pane up with something 15 │ +06 (C): │fill pane up with something 16 │ +07 (C): │fill pane up with something 17 │ +08 (C): │fill pane up with something 18 │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-5.snap new file mode 100644 index 0000000000..9c7f4cf78e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-5.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1175 +expression: "format!(\"{}\", snapshot_count)" +--- +4 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action.snap new file mode 100644 index 0000000000..94eef91e5f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1147 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_switch_mode_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_switch_mode_action.snap new file mode 100644 index 0000000000..49e3d8fb10 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_switch_mode_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1711 +expression: "format!(\"{:?}\", *\n mock_screen.os_input.server_to_client_messages.lock().unwrap())" +--- +{1: [SwitchToMode(Locked)]} diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap new file mode 100644 index 0000000000..8597c63091 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1487 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[Write([102, 111, 111], 0), Write([102, 111, 111], 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-2.snap new file mode 100644 index 0000000000..e20094c234 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1775 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-3.snap new file mode 100644 index 0000000000..105baa7f43 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1775 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ ┌ Pane #1 ─────────────────────────────┐ │ +04 (C): │ │ │ │ +05 (C): │ │ │ │ +06 (C): │ │ │ │ +07 (C): │ └──────────────────────────────────────┘ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-4.snap new file mode 100644 index 0000000000..84f723239e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1775 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-5.snap new file mode 100644 index 0000000000..105baa7f43 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-5.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1775 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ ┌ Pane #1 ─────────────────────────────┐ │ +04 (C): │ │ │ │ +05 (C): │ │ │ │ +06 (C): │ │ │ │ +07 (C): │ └──────────────────────────────────────┘ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-6.snap new file mode 100644 index 0000000000..2b28bb3a1a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-6.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1818 +expression: "format!(\"{}\", snapshot_count)" +--- +5 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes.snap new file mode 100644 index 0000000000..b7d2d25848 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1775 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-2.snap new file mode 100644 index 0000000000..d7a846285c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1427 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-3.snap new file mode 100644 index 0000000000..25e1f8df25 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1427 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-4.snap new file mode 100644 index 0000000000..8ee7c1cbc0 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1466 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action.snap new file mode 100644 index 0000000000..a18e57fb20 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1427 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-2.snap new file mode 100644 index 0000000000..0cffa2c9a3 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1737 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-3.snap new file mode 100644 index 0000000000..81d9b2596c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1737 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ ┌ Pane #1 ─────────────────────────────┐ │ +04 (C): │ │ │ │ +05 (C): │ │ │ │ +06 (C): │ │ │ │ +07 (C): │ └──────────────────────────────────────┘ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-4.snap new file mode 100644 index 0000000000..3ee6542fd4 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1737 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #2 ─────────────────────────────┐┌ Pane #1 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-5.snap new file mode 100644 index 0000000000..0f07af8a45 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-5.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1778 +expression: "format!(\"{}\", snapshot_count)" +--- +4 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float.snap new file mode 100644 index 0000000000..626bca745c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1737 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-2.snap new file mode 100644 index 0000000000..2e0fc9156f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1457 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-3.snap new file mode 100644 index 0000000000..395ce350bc --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1457 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): │ +01 (C): │ +02 (C): │ +03 (C): │ +04 (C): │ +05 (C): │ +06 (C): │ +07 (C): │ +08 (C): │ +09 (C): │ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-4.snap new file mode 100644 index 0000000000..2ca593dd2a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-4.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1498 +expression: "format!(\"{}\", snapshot_count)" +--- +3 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action.snap new file mode 100644 index 0000000000..e8307ea91a --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1457 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-2.snap new file mode 100644 index 0000000000..f8d04616fb --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-2.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1870 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): +01 (C): +02 (C): +03 (C): +04 (C): +05 (C): +06 (C): +07 (C): +08 (C): +09 (C): + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-3.snap new file mode 100644 index 0000000000..37c5f37187 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-3.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1870 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ my_new_pane_title ───────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-4.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-4.snap new file mode 100644 index 0000000000..a65e528ee1 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-4.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1870 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap new file mode 100644 index 0000000000..cedc817c13 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1919 +expression: "format!(\"{}\", snapshot_count)" +--- +4 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action.snap new file mode 100644 index 0000000000..3457c90d5b --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action.snap @@ -0,0 +1,16 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1870 +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap new file mode 100644 index 0000000000..d7950b1d55 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap @@ -0,0 +1,473 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2190 +expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" +--- +[ + Update( + None, + Some( + 10, + ), + InputReceived, + ), + Update( + None, + Some( + 10, + ), + InputReceived, + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + ModeUpdate( + ModeInfo { + mode: Normal, + keybinds: [], + style: Style { + colors: Palette { + source: Default, + theme_hue: Dark, + fg: EightBit( + 0, + ), + bg: EightBit( + 0, + ), + black: EightBit( + 0, + ), + red: EightBit( + 0, + ), + green: EightBit( + 0, + ), + yellow: EightBit( + 0, + ), + blue: EightBit( + 0, + ), + magenta: EightBit( + 0, + ), + cyan: EightBit( + 0, + ), + white: EightBit( + 0, + ), + orange: EightBit( + 0, + ), + gray: EightBit( + 0, + ), + purple: EightBit( + 0, + ), + gold: EightBit( + 0, + ), + silver: EightBit( + 0, + ), + pink: EightBit( + 0, + ), + brown: EightBit( + 0, + ), + }, + rounded_corners: false, + }, + capabilities: PluginCapabilities { + arrow_fonts: false, + }, + session_name: Some( + "zellij", + ), + }, + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "Tab #2", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "new-tab-name", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Update( + None, + Some( + 10, + ), + InputReceived, + ), + Update( + None, + Some( + 1, + ), + TabUpdate( + [ + TabInfo { + position: 0, + name: "Tab #1", + active: false, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + TabInfo { + position: 1, + name: "Tab #2", + active: true, + panes_to_hide: 0, + is_fullscreen_active: false, + is_sync_panes_active: false, + are_floating_panes_visible: false, + other_focused_clients: [], + }, + ], + ), + ), + Exit, +] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap new file mode 100644 index 0000000000..369080d8d3 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 2221 +expression: "format!(\"{:?}\", * received_server_instructions.lock().unwrap())" +--- +[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap new file mode 100644 index 0000000000..32ae2a500d --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 879 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[Write([102, 111, 111], 0), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap new file mode 100644 index 0000000000..90ade0ee52 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 846 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[Write([105, 110, 112, 117, 116, 32, 102, 114, 111, 109, 32, 116, 104, 101, 32, 99, 108, 105], 0), Exit] diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 908b296761..2590e1f9d2 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -2,7 +2,9 @@ use crate::setup::Setup; use crate::{ consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV}, input::options::CliOptions, + input::actions::{Action, ResizeDirection, Direction}, }; +use crate::data::InputMode; use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -119,6 +121,116 @@ pub enum Sessions { yes: bool, }, /// Send actions to a specific session - #[cfg(feature = "unstable")] - Action { action: Option }, + #[clap(visible_alias = "ac")] + #[clap(subcommand)] + Action(CliAction), +} + +#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] +pub enum CliAction { + /// Quit Zellij. + Quit, + /// Write bytes to the terminal. + Write { bytes: Vec }, + /// Write characters to the terminal. + WriteChars { chars: String }, + /// Resize the focused pane in the specified direction. [right|left|up|down|+|-] + Resize { resize_direction: ResizeDirection }, + /// Change focus to the next pane + FocusNextPane, + /// Change focus to the previous pane + FocusPreviousPane, + /// Move the focused pane in the specified direction. [right|left|up|down] + MoveFocus { direction: Direction }, + /// Move focus to the pane or tab (if on screen edge) in the specified direction + /// [right|left|up|down] + MoveFocusOrTab { direction: Direction }, + /// Change the location of the focused pane in the specified direction + /// [right|left|up|down] + MovePane { direction: Direction }, + /// Dumps the pane scrollback to a file + DumpScreen { path: PathBuf }, + /// Open the pane scrollback in your default editor + EditScrollback, + /// Scroll up in the focused pane + ScrollUp, + /// Scroll down in focus pane. + ScrollDown, + /// Scroll down to bottom in focus pane. + ScrollToBottom, + /// Scroll up one page in focus pane. + PageScrollUp, + /// Scroll down one page in focus pane. + PageScrollDown, + /// Scroll up half page in focus pane. + HalfPageScrollUp, + /// Scroll down half page in focus pane. + HalfPageScrollDown, + /// Toggle between fullscreen focus pane and normal layout. + ToggleFullscreen, + /// Toggle frames around panes in the UI + TogglePaneFrames, + /// Toggle between sending text commands to all panes on the current tab and normal mode. + ToggleActiveSyncTab, + /// Open a new pane in the specified direction [right|left|up|down] + /// If no direction is specified, will try to use the biggest available space. + NewPane { + #[clap(short, long, value_parser, conflicts_with("floating"))] + direction: Option, + #[clap(short, long, value_parser)] + command: Option, + #[clap(long, value_parser)] + cwd: Option, + #[clap(short, long, value_parser)] + args: Option, + #[clap(short, long, value_parser, default_missing_value("true"))] + floating: Option, + }, + /// Open the specified file in a new zellij pane with your default EDITOR + Edit { + file: PathBuf, + #[clap(short, long, value_parser, conflicts_with("floating"))] + direction: Option, + #[clap(short, long, value_parser)] + line_number: Option, + #[clap(short, long, value_parser, default_missing_value("true"))] + floating: Option, + }, + /// Switch input mode of all connected clients [locked|pane|tab|resize|move|search|session] + SwitchMode { input_mode: InputMode }, + /// Embed focused pane if floating or float focused pane if embedded + TogglePaneEmbedOrFloating, + /// Toggle the visibility of all fdirectionloating panes in the current Tab, open one if none exist + ToggleFloatingPanes, + /// Close the focused pane. + ClosePane, + /// Renames the focused pane + RenamePane { name: String }, + /// Remove a previously set pane name + UndoRenamePane, + /// Go to the next tab. + GoToNextTab, + /// Go to the previous tab. + GoToPreviousTab, + /// Close the current tab. + CloseTab, + /// Go to tab with index [index] + GoToTab { index: u32 }, + /// Renames the focused pane + RenameTab { name: String }, + /// Remove a previously set tab name + UndoRenameTab, + /// Search for String + SearchInput { input: String }, + /// Focus on next search occurrence + SearchNext, + /// Focus on previous search occurrence + SearchPrevious, + /// Create a new tab, optionally with a specified tab layout and name + NewTab { + #[clap(short, long, value_parser)] + layout: Option, + #[clap(short, long, value_parser)] + name: Option + }, } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 717803c25a..610e667b73 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -1,3 +1,4 @@ +use crate::input::config::ConversionError; use crate::input::actions::Action; use clap::ArgEnum; use serde::{Deserialize, Serialize}; @@ -342,9 +343,9 @@ impl Default for PaletteColor { } impl FromStr for InputMode { - type Err = Box; + type Err = ConversionError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s { "normal" | "Normal" => Ok(InputMode::Normal), "locked" | "Locked" => Ok(InputMode::Locked), @@ -360,7 +361,7 @@ impl FromStr for InputMode { "prompt" | "Prompt" => Ok(InputMode::Prompt), "tmux" | "Tmux" => Ok(InputMode::Tmux), "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch), - e => Err(format!("unknown mode \"{}\"", e).into()), + e => Err(ConversionError::UnknownInputMode(e.into())), } } } diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index 1cd203efe0..a941d42acb 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -5,6 +5,7 @@ use std::{ collections::{HashMap, BTreeMap}, env::{set_var, var}, }; +use crate::data::ClientId; use std::fmt; @@ -17,6 +18,7 @@ pub fn set_zellij(v: String) { } pub const SESSION_NAME_ENV_KEY: &str = "ZELLIJ_SESSION_NAME"; +pub const CLIENT_ID_ENV_VAR: &str = "ZELLIJ_CLIENT_ID"; pub fn get_session_name() -> Result { Ok(var(SESSION_NAME_ENV_KEY)?) @@ -26,6 +28,15 @@ pub fn set_session_name(v: String) { set_var(SESSION_NAME_ENV_KEY, v); } +pub fn set_client_id(client_id: ClientId) { + log::info!("set_client_id: {:?}", client_id); + set_var(CLIENT_ID_ENV_VAR, format!("{:?}", client_id)); +} + +pub fn get_client_id() -> Result { + Ok(var(CLIENT_ID_ENV_VAR)?) +} + pub fn set_initial_environment_vars() { set_var("COLORTERM", "24bit"); } diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 07dfb89e02..01995c3348 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -231,6 +231,8 @@ pub enum ScreenContext { NewPane, OpenInPlaceEditor, ToggleFloatingPanes, + ShowFloatingPanes, + HideFloatingPanes, TogglePaneEmbedOrFloating, HorizontalSplit, VerticalSplit, @@ -293,6 +295,7 @@ pub enum ScreenContext { TerminalForegroundColor, TerminalColorRegisters, ChangeMode, + ChangeModeForAllClients, LeftClick, RightClick, MiddleClick, @@ -356,6 +359,7 @@ pub enum ClientContext { SwitchToMode, Connected, ActiveClients, + OwnClientId, } /// Stack call representations corresponding to the different types of [`ServerInstruction`]s. diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 9b210dd9c4..c268825db8 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,12 +1,14 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::PaneLayout; +use super::layout::{Layout, PaneLayout}; +use crate::cli::CliAction; use crate::data::InputMode; use crate::input::options::OnForceClose; use serde::{Deserialize, Serialize}; use std::str::FromStr; +use std::path::PathBuf; use crate::position::Position; @@ -22,10 +24,10 @@ impl FromStr for Direction { type Err = String; fn from_str(s: &str) -> Result { match s { - "Left" => Ok(Direction::Left), - "Right" => Ok(Direction::Right), - "Up" => Ok(Direction::Up), - "Down" => Ok(Direction::Down), + "Left" | "left" => Ok(Direction::Left), + "Right" | "right" => Ok(Direction::Right), + "Up" | "up" => Ok(Direction::Up), + "Down" | "down" => Ok(Direction::Down), _ => Err(format!("Failed to parse Direction. Unknown Direction: {}", s)), } } @@ -44,12 +46,12 @@ impl FromStr for ResizeDirection { type Err = String; fn from_str(s: &str) -> Result { match s { - "Left" => Ok(ResizeDirection::Left), - "Right" => Ok(ResizeDirection::Right), - "Up" => Ok(ResizeDirection::Up), - "Down" => Ok(ResizeDirection::Down), - "Increase" => Ok(ResizeDirection::Increase), - "Decrease" => Ok(ResizeDirection::Decrease), + "Left" | "left" => Ok(ResizeDirection::Left), + "Right" | "right" => Ok(ResizeDirection::Right), + "Up" | "up" => Ok(ResizeDirection::Up), + "Down" | "down" => Ok(ResizeDirection::Down), + "Increase" | "increase" | "+" => Ok(ResizeDirection::Increase), + "Decrease" | "decrease" | "-" => Ok(ResizeDirection::Decrease), _ => Err(format!("Failed to parse ResizeDirection. Unknown ResizeDirection: {}", s)), } } @@ -91,6 +93,39 @@ impl FromStr for SearchOption { } } +fn split_escaped_whitespace(s: &str) -> Vec { + s.split_ascii_whitespace().map(|s| String::from(s)) + .fold(vec![], |mut acc, part| { + if let Some(previous_part) = acc.last_mut() { + if previous_part.ends_with('\\') { + previous_part.push(' '); + previous_part.push_str(&part); + return acc; + } + } + acc.push(part); + acc + }) +} + +fn split_command_and_args(command: String, args: Option) -> (PathBuf, Vec) { + let mut full_command = split_escaped_whitespace(&command); + let mut command = None; + let mut command_args = vec![]; + for part in full_command.drain(..) { + if command.is_none() { + command = Some(part); + } else { + command_args.push(part); + } + } + let command = PathBuf::from(command.unwrap()); + if let Some(additional_args) = args.map(|args| split_escaped_whitespace(&args)).as_mut() { + command_args.append(additional_args); + } + (command, command_args) +} + // As these actions are bound to the default config, please // do take care when refactoring - or renaming. // They might need to be adjusted in the default config @@ -106,6 +141,8 @@ pub enum Action { WriteChars(String), /// Switch to the specified input mode. SwitchToMode(InputMode), + /// Switch all connected clients to the specified input mode. + SwitchModeForAllClients(InputMode), /// Resize focus pane in specified direction. Resize(ResizeDirection), /// Switch focus to next pane in specified direction. @@ -148,6 +185,10 @@ pub enum Action { /// Open a new pane in the specified direction (relative to focus). /// If no direction is specified, will try to use the biggest available space. NewPane(Option), + /// Open the file in a new pane using the default editor + EditFile(PathBuf, Option, Option, Option), // usize is an optional line number, bool is floating true/false + /// Open a new floating pane + NewFloatingPane(Option), /// Embed focused pane in tab if floating or float focused pane if embedded TogglePaneEmbedOrFloating, /// Toggle the visibility of all floating panes (if any) in the current Tab @@ -198,6 +239,90 @@ pub enum Action { SearchToggleOption(SearchOption), } +impl Action { + pub fn actions_from_cli(cli_action: CliAction) -> Result, String> { + match cli_action { + CliAction::Quit => Ok(vec![Action::Quit]), + CliAction::Write { bytes } => Ok(vec![Action::Write(bytes)]), + CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]), + CliAction::Resize { resize_direction } => Ok(vec![Action::Resize(resize_direction)]), + CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]), + CliAction::FocusPreviousPane => Ok(vec![Action::FocusPreviousPane]), + CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus(direction)]), + CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]), + CliAction::MovePane { direction } => Ok(vec![Action::MovePane(Some(direction))]), + CliAction::DumpScreen { path } => Ok(vec![Action::DumpScreen(path.as_os_str().to_string_lossy().into())]), + CliAction::EditScrollback => Ok(vec![Action::EditScrollback]), + CliAction::ScrollUp => Ok(vec![Action::ScrollUp]), + CliAction::ScrollDown => Ok(vec![Action::ScrollDown]), + CliAction::ScrollToBottom => Ok(vec![Action::ScrollToBottom]), + CliAction::PageScrollUp => Ok(vec![Action::PageScrollUp]), + CliAction::PageScrollDown => Ok(vec![Action::PageScrollDown]), + CliAction::HalfPageScrollUp => Ok(vec![Action::HalfPageScrollUp]), + CliAction::HalfPageScrollDown => Ok(vec![Action::HalfPageScrollDown]), + CliAction::ToggleFullscreen => Ok(vec![Action::ToggleFocusFullscreen]), + CliAction::TogglePaneFrames => Ok(vec![Action::TogglePaneFrames]), + CliAction::ToggleActiveSyncTab => Ok(vec![Action::ToggleActiveSyncTab]), + CliAction::NewPane { direction, command, cwd, args, floating } => { + match command { + Some(command) => { + let (command, args) = split_command_and_args(command, args); + let run_command_action = RunCommandAction { command, args, cwd, direction }; + match floating { + Some(true) => Ok(vec![Action::NewFloatingPane(Some(run_command_action))]), + _ => Ok(vec![Action::Run(run_command_action)]), + } + }, + None => { + match floating { + Some(true) => Ok(vec![Action::NewFloatingPane(None)]), + _ => Ok(vec![Action::NewPane(direction)]), + } + } + } + } + CliAction::Edit { direction, file, line_number, floating } => Ok(vec![Action::EditFile(file, line_number, direction, floating)]), + CliAction::SwitchMode { input_mode } => Ok(vec![Action::SwitchModeForAllClients(input_mode)]), + CliAction::TogglePaneEmbedOrFloating => Ok(vec![Action::TogglePaneEmbedOrFloating]), + CliAction::ToggleFloatingPanes => Ok(vec![Action::ToggleFloatingPanes]), + CliAction::ClosePane => Ok(vec![Action::CloseFocus]), + CliAction::RenamePane { name }=> Ok(vec![Action::PaneNameInput(name.as_bytes().to_vec())]), + CliAction::UndoRenamePane => Ok(vec![Action::UndoRenamePane]), + CliAction::GoToNextTab => Ok(vec![Action::GoToNextTab]), + CliAction::GoToPreviousTab => Ok(vec![Action::GoToPreviousTab]), + CliAction::CloseTab => Ok(vec![Action::CloseTab]), + CliAction::GoToTab { index } => Ok(vec![Action::GoToTab(index)]), + CliAction::RenameTab { name } => Ok(vec![ + Action::TabNameInput(vec![0]), + Action::TabNameInput(name.as_bytes().to_vec()) + ]), + CliAction::UndoRenameTab => Ok(vec![Action::UndoRenameTab]), + CliAction::SearchInput { input } => Ok(vec![Action::SearchInput(input.as_bytes().to_vec())]), + CliAction::SearchNext => Ok(vec![Action::Search(SearchDirection::Down)]), + CliAction::SearchPrevious => Ok(vec![Action::Search(SearchDirection::Up)]), + CliAction::NewTab { name, layout } => { + if let Some(layout_path) = layout { + let raw_layout = Layout::stringified_from_path_or_default(Some(&layout_path), None).map_err(|e| format!("Failed to load layout: {}", e))?; + let layout = Layout::from_str(&raw_layout).map_err(|e| format!("Failed to parse layout: {}", e))?; + let mut tabs = layout.tabs(); + if tabs.len() > 1 { + return Err(format!("Tab layout cannot itself have tabs")); + } else if !tabs.is_empty() { + let (tab_name, layout) = tabs.drain(..).next().unwrap(); + let name = tab_name.or(name); + Ok(vec![Action::NewTab(Some(layout), name)]) + } else { + let layout = layout.new_tab(); + Ok(vec![Action::NewTab(Some(layout), name)]) + } + } else { + Ok(vec![Action::NewTab(None, name)]) + } + } + } + } +} + impl From for Action { fn from(ofc: OnForceClose) -> Action { match ofc { diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 8b00f253e7..95c6eb863e 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -50,6 +50,14 @@ pub enum ConfigError { // Plugins have a semantic error, usually trying to parse two of the same tag #[error("PluginsError: {0}")] PluginsError(#[from] PluginsConfigError), + #[error("{0}")] + ConversionError(#[from] ConversionError), +} + +#[derive(Debug, Error)] +pub enum ConversionError { + #[error("{0}")] + UnknownInputMode(String), } impl TryFrom<&CliArgs> for Config { diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap index 8ced6a81bd..c001b5f3e4 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/keybinds_test.rs -assertion_line: 475 +assertion_line: 476 expression: "format!(\"{:?}\", config_error)" --- -Std("unknown mode \"i_do_not_exist\"") +ConversionError(UnknownInputMode("i_do_not_exist")) From b6e082c07cd24b6bec746c0063c393b2306c91fb Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 15 Sep 2022 12:49:22 +0200 Subject: [PATCH 32/55] fix(cli): exit only when action is done --- zellij-client/src/cli_client.rs | 17 ++++++++++++----- zellij-utils/src/envs.rs | 11 ----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/zellij-client/src/cli_client.rs b/zellij-client/src/cli_client.rs index 7da09243ee..a6d02195de 100644 --- a/zellij-client/src/cli_client.rs +++ b/zellij-client/src/cli_client.rs @@ -1,15 +1,14 @@ //! The `[cli_client]` is used to attach to a running server session //! and dispatch actions, that are specified through the command line. use std::{fs, path::PathBuf}; -use zellij_utils::envs; +use std::process; use crate::{ os_input_output::ClientOsApi, }; use zellij_utils::{ - data::ClientId, input::actions::Action, - ipc::ClientToServerMsg, + ipc::{ClientToServerMsg, ServerToClientMsg}, }; pub fn start_cli_client( @@ -17,7 +16,6 @@ pub fn start_cli_client( session_name: &str, actions: Vec, ) { - let client_id: Option = envs::get_client_id().ok().map(|s| s.encode_utf16().next().unwrap()); // TODO: better? does this even work? let zellij_ipc_pipe: PathBuf = { let mut sock_dir = zellij_utils::consts::ZELLIJ_SOCK_DIR.clone(); fs::create_dir_all(&sock_dir).unwrap(); @@ -27,7 +25,16 @@ pub fn start_cli_client( }; os_input.connect_to_server(&*zellij_ipc_pipe); for action in actions { - let msg = ClientToServerMsg::Action(action, client_id); + let msg = ClientToServerMsg::Action(action, None); os_input.send_to_server(msg); } + loop { + match os_input.recv_from_server() { + Some((ServerToClientMsg::UnblockInputThread, _)) => { + os_input.send_to_server(ClientToServerMsg::ClientExited); + process::exit(0); + }, + _ => {} + } + } } diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index a941d42acb..1cd203efe0 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -5,7 +5,6 @@ use std::{ collections::{HashMap, BTreeMap}, env::{set_var, var}, }; -use crate::data::ClientId; use std::fmt; @@ -18,7 +17,6 @@ pub fn set_zellij(v: String) { } pub const SESSION_NAME_ENV_KEY: &str = "ZELLIJ_SESSION_NAME"; -pub const CLIENT_ID_ENV_VAR: &str = "ZELLIJ_CLIENT_ID"; pub fn get_session_name() -> Result { Ok(var(SESSION_NAME_ENV_KEY)?) @@ -28,15 +26,6 @@ pub fn set_session_name(v: String) { set_var(SESSION_NAME_ENV_KEY, v); } -pub fn set_client_id(client_id: ClientId) { - log::info!("set_client_id: {:?}", client_id); - set_var(CLIENT_ID_ENV_VAR, format!("{:?}", client_id)); -} - -pub fn get_client_id() -> Result { - Ok(var(CLIENT_ID_ENV_VAR)?) -} - pub fn set_initial_environment_vars() { set_var("COLORTERM", "24bit"); } From c559fb9f67775a040d17a4882da3e0872f511863 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 15 Sep 2022 14:19:22 +0200 Subject: [PATCH 33/55] fix(cli): open embedded pane from floating pane --- zellij-server/src/route.rs | 28 +++++++++++++++++++ ...pane_action_with_command_cwd_and_args.snap | 4 +-- ...w_pane_action_with_default_parameters.snap | 4 +-- ..._new_pane_action_with_split_direction.snap | 4 +-- zellij-utils/src/input/actions.rs | 8 ++++-- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 8f22afb6eb..14c4155e6c 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -306,6 +306,34 @@ pub(crate) fn route_action( }); session.senders.send_to_pty(PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id))).unwrap(); }, + Action::NewTiledPane(direction, run_command) => { + session + .senders + .send_to_screen(ScreenInstruction::HideFloatingPanes(client_id)) + .unwrap(); + let run_cmd = run_command.map(|cmd| TerminalAction::RunCommand(cmd.into())).or_else(|| { + session.default_shell.clone() + }); + let pty_instr = match direction { + Some(Direction::Left) => { + PtyInstruction::SpawnTerminalVertically(run_cmd, client_id) + }, + Some(Direction::Right) => { + PtyInstruction::SpawnTerminalVertically(run_cmd, client_id) + }, + Some(Direction::Up) => { + PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id) + }, + Some(Direction::Down) => { + PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id) + }, + // No direction specified - try to put it in the biggest available spot + None => { + PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id)) + }, + }; + session.senders.send_to_pty(pty_instr).unwrap(); + }, Action::TogglePaneEmbedOrFloating => { session .senders diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap index 4f577fef16..29b09a8b45 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1586 +assertion_line: 1632 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: ["-h", "--something", "arg"], cwd: Some("/some/folder") })), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: ["-h", "--something", "arg"], cwd: Some("/some/folder") })), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap index bdb1bd7e95..eedc6c50aa 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1520 +assertion_line: 1566 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminal(None, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminal(None, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap index 6e7c599d7b..de7a246596 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1553 +assertion_line: 1599 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminalVertically(None, 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminalVertically(None, 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index c268825db8..9b729b8b2f 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; use crate::position::Position; /// The four directions (left, right, up, down). -#[derive(Eq, Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Eq, Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] pub enum Direction { Left, Right, @@ -189,6 +189,8 @@ pub enum Action { EditFile(PathBuf, Option, Option, Option), // usize is an optional line number, bool is floating true/false /// Open a new floating pane NewFloatingPane(Option), + /// Open a new tiled (embedded, non-floating) pane + NewTiledPane(Option, Option), /// Embed focused pane in tab if floating or float focused pane if embedded TogglePaneEmbedOrFloating, /// Toggle the visibility of all floating panes (if any) in the current Tab @@ -270,13 +272,13 @@ impl Action { let run_command_action = RunCommandAction { command, args, cwd, direction }; match floating { Some(true) => Ok(vec![Action::NewFloatingPane(Some(run_command_action))]), - _ => Ok(vec![Action::Run(run_command_action)]), + _ => Ok(vec![Action::NewTiledPane(direction, Some(run_command_action))]), } }, None => { match floating { Some(true) => Ok(vec![Action::NewFloatingPane(None)]), - _ => Ok(vec![Action::NewPane(direction)]), + _ => Ok(vec![Action::NewTiledPane(direction, None)]), } } } From 4c751a09c3290c794d24654ff1315a8fc8b3f9d4 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 15 Sep 2022 14:50:41 +0200 Subject: [PATCH 34/55] fix(cli): send actions to other sessions --- src/commands.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 40863feec1..d2ac7b40d5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -119,23 +119,37 @@ fn find_indexed_session( } } -pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, session_name: Option) { +pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, requested_session_name: Option) { match get_active_session() { ActiveSession::None => { eprintln!("There is no active session!"); std::process::exit(1); }, ActiveSession::One(session_name) => { + if let Some(requested_session_name) = requested_session_name { + if requested_session_name != session_name { + eprintln!("Session '{}' not found. The following sessions are active:", requested_session_name); + eprintln!("{}", session_name); + std::process::exit(1); + } + } attach_with_cli_client(cli_action, &session_name); }, ActiveSession::Many => { - if let Some(session_name) = session_name { - attach_with_cli_client(cli_action, &session_name); + let existing_sessions = get_sessions().unwrap(); + if let Some(session_name) = requested_session_name { + if existing_sessions.contains(&session_name) { + attach_with_cli_client(cli_action, &session_name); + } else { + eprintln!("Session '{}' not found. The following sessions are active:", session_name); + print_sessions(existing_sessions); + std::process::exit(1); + } } else if let Ok(session_name) = envs::get_session_name() { attach_with_cli_client(cli_action, &session_name); } else { - println!("Please specify the session name to send actions to. The following sessions are active:"); - print_sessions(get_sessions().unwrap()); + eprintln!("Please specify the session name to send actions to. The following sessions are active:"); + print_sessions(existing_sessions); std::process::exit(1); } }, From 02a8b9d1e670d377bc6222ff58d6bcaa299a4d78 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 16 Sep 2022 11:51:42 +0200 Subject: [PATCH 35/55] feat(cli): command alias --- src/main.rs | 13 ++++++++++++- zellij-utils/src/cli.rs | 13 +++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index f503e8492e..d59e07d434 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod tests; use zellij_utils::{ clap::Parser, - cli::{CliArgs, Command, Sessions}, + cli::{CliArgs, CliAction, Command, Sessions}, logging::*, }; @@ -19,6 +19,17 @@ fn main() { commands::send_action_to_session(cli_action, opts.session); std::process::exit(0); } + if let Some(Command::Sessions(Sessions::Command{ command, direction, cwd, args, floating })) = opts.command { + let command_cli_action = CliAction::NewPane { + command, + direction, + cwd, + args, + floating, + }; + commands::send_action_to_session(command_cli_action, opts.session); + std::process::exit(0); + } } if let Some(Command::Sessions(Sessions::ListSessions)) = opts.command { diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 2590e1f9d2..d2cfe60342 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -124,6 +124,19 @@ pub enum Sessions { #[clap(visible_alias = "ac")] #[clap(subcommand)] Action(CliAction), + /// Send actions to a specific session + #[clap(visible_alias = "c")] + Command { + command: Option, + #[clap(short, long, value_parser, conflicts_with("floating"))] + direction: Option, + #[clap(long, value_parser)] + cwd: Option, + #[clap(short, long, value_parser)] + args: Option, + #[clap(short, long, value_parser, default_missing_value("true"))] + floating: Option, + } } #[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] From a4e9ee0e2baf1cafb0bb8ed6e50d2f60f41e2d32 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Tue, 20 Sep 2022 09:42:37 +0200 Subject: [PATCH 36/55] feat(converter): convert old config --- Cargo.lock | 3 + src/commands.rs | 31 + src/main.rs | 4 + zellij-client/Cargo.toml | 3 + zellij-client/src/lib.rs | 1 + zellij-client/src/old_config_converter/mod.rs | 2 + .../src/old_config_converter/old_config.rs | 1146 +++++++++++++++++ .../src/old_config_converter/old_layout.rs | 820 ++++++++++++ .../unit/convert_config_tests.rs | 92 ++ .../fixtures/old_default_yaml_config.yaml | 639 +++++++++ .../old_yaml_config_with_custom_options.yaml | 639 +++++++++ .../old_yaml_config_with_env_variables.yaml | 644 +++++++++ ...ml_config_with_global_keybind_unbinds.yaml | 639 +++++++++ .../fixtures/old_yaml_config_with_themes.yaml | 677 ++++++++++ .../fixtures/old_yaml_config_with_ui.yaml | 643 +++++++++ ..._config_with_unbind_all_keys_per_mode.yaml | 639 +++++++++ .../old_yaml_config_with_unbinds_in_mode.yaml | 640 +++++++++ ...t__convert_config_with_custom_options.snap | 411 ++++++ ...st__convert_config_with_env_variables.snap | 415 ++++++ ...rt_config_with_global_keybind_unbinds.snap | 411 ++++++ ...t_config_with_keybind_unbinds_in_mode.snap | 411 ++++++ ...st__convert_config_with_themes_config.snap | 451 +++++++ ...g_test__convert_config_with_ui_config.snap | 416 ++++++ ..._config_with_unbind_all_keys_per_mode.snap | 410 ++++++ ...test__properly_convert_default_config.snap | 410 ++++++ zellij-utils/assets/config/default.kdl | 13 + zellij-utils/src/cli.rs | 5 + zellij-utils/src/kdl/mod.rs | 9 +- 28 files changed, 10618 insertions(+), 6 deletions(-) create mode 100644 zellij-client/src/old_config_converter/mod.rs create mode 100644 zellij-client/src/old_config_converter/old_config.rs create mode 100644 zellij-client/src/old_config_converter/old_layout.rs create mode 100644 zellij-client/src/old_config_converter/unit/convert_config_tests.rs create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_custom_options.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_env_variables.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_global_keybind_unbinds.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_keybind_unbinds_in_mode.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_themes_config.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_ui_config.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_unbind_all_keys_per_mode.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__properly_convert_default_config.snap diff --git a/Cargo.lock b/Cargo.lock index ff3ee157d5..2db0fa2b84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3412,6 +3412,9 @@ dependencies = [ "insta", "log", "mio", + "serde", + "serde_yaml", + "url", "zellij-utils", ] diff --git a/src/commands.rs b/src/commands.rs index d2ac7b40d5..eeb1c8205e 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; use std::process; use zellij_utils::input::actions::Action; use zellij_client::start_client as start_client_impl; +use zellij_client::old_config_converter::config_yaml_to_config_kdl; use zellij_client::{os_input_output::get_client_os_input, ClientInfo}; use zellij_server::os_input_output::get_server_os_input; use zellij_server::start_server as start_server_impl; @@ -22,6 +23,8 @@ use zellij_utils::{ setup::{get_default_data_dir, Setup}, }; +use std::{fs::File, io::prelude::*}; + #[cfg(feature = "unstable")] use miette::IntoDiagnostic; #[cfg(feature = "unstable")] @@ -155,6 +158,34 @@ pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, r }, }; } +pub(crate) fn convert_old_config_file(old_config_file: PathBuf, output_location: Option) { + match File::open(&old_config_file) { + Ok(mut handle) => { + let mut raw_config_file = String::new(); + let _ = handle.read_to_string(&mut raw_config_file); + match config_yaml_to_config_kdl(&raw_config_file) { + Ok(kdl_config) => { + // TODO: CONTINUE HERE (18/09) + // - print/error in the main function (main.rs?) - DONE + // - add tests for Options + Keybinds - DONE + // - write conversions + tests for everything else - DONE + // - refactor and move on to converting layouts + println!("{}", kdl_config); + process::exit(0); + }, + Err(e) => { + eprintln!("Failed to convert config: {}", e); + process::exit(1); + } + } + }, + Err(e) => { + eprintln!("Failed to open file: {}", e); + process::exit(1); + } + } + +} fn attach_with_cli_client(cli_action: zellij_utils::cli::CliAction, session_name: &str) { let os_input = get_os_input(zellij_client::os_input_output::get_client_os_input); diff --git a/src/main.rs b/src/main.rs index d59e07d434..43a610c36a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,10 @@ fn main() { commands::send_action_to_session(command_cli_action, opts.session); std::process::exit(0); } + if let Some(Command::Sessions(Sessions::Convert{ old_config_file, output })) = opts.command { + commands::convert_old_config_file(old_config_file, output); + std::process::exit(0); + } } if let Some(Command::Sessions(Sessions::ListSessions)) = opts.command { diff --git a/zellij-client/Cargo.toml b/zellij-client/Cargo.toml index 30370d47e2..5761b0e59b 100644 --- a/zellij-client/Cargo.toml +++ b/zellij-client/Cargo.toml @@ -10,6 +10,9 @@ license = "MIT" [dependencies] mio = { version = "0.7.11", features = ['os-ext'] } +serde = { version = "1.0", features = ["derive"] } +url = { version = "2.2.2", features = ["serde"] } +serde_yaml = "0.8" zellij-utils = { path = "../zellij-utils/", version = "0.32.0" } log = "0.4.17" diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 52e5fd1f48..dcc12490ce 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -6,6 +6,7 @@ mod input_handler; mod sessions; mod stdin_ansi_parser; mod stdin_handler; +pub mod old_config_converter; use log::info; use std::env::current_exe; diff --git a/zellij-client/src/old_config_converter/mod.rs b/zellij-client/src/old_config_converter/mod.rs new file mode 100644 index 0000000000..4cb24b741c --- /dev/null +++ b/zellij-client/src/old_config_converter/mod.rs @@ -0,0 +1,2 @@ +mod old_config; +pub use old_config::config_yaml_to_config_kdl; diff --git a/zellij-client/src/old_config_converter/old_config.rs b/zellij-client/src/old_config_converter/old_config.rs new file mode 100644 index 0000000000..7661154241 --- /dev/null +++ b/zellij-client/src/old_config_converter/old_config.rs @@ -0,0 +1,1146 @@ +// This is a converter from the old yaml config to the new KDL config. +// +// It is supposed to be mostly self containing - please refrain from adding to it, importing +// from it or changing it +use std::fmt; +use std::path::PathBuf; +// use thiserror::Error; + +use url::Url; +use serde::{Deserialize, Deserializer, Serialize}; +use serde::de::{Error, Visitor}; +use std::collections::{HashMap, BTreeMap}; + +const ON_FORCE_CLOSE_DESCRIPTION: &'static str = " +// Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP +// eg. when terminal window with an active zellij session is closed +// Options: +// - detach (Default) +// - quit +// +"; + +const SIMPLIFIED_UI_DESCRIPTION: &'static str = " +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +"; + +const DEFAULT_SHELL_DESCRIPTION: &'static str = " +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +"; + +const PANE_FRAMES_DESCRIPTION: &'static str = " +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +"; + +const DEFAULT_THEME_DESCRIPTION: &'static str = " +// Choose the theme that is specified in the themes section. +// Default: default +// +"; + +const DEFAULT_MODE_DESCRIPTION: &'static str = " +// Choose the mode that zellij uses when starting up. +// Default: normal +// +"; + +const MOUSE_MODE_DESCRIPTION: &'static str = " +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +"; + +const SCROLL_BUFFER_SIZE_DESCRIPTION: &'static str = " +// Configure the scroll back buffer size +// This is the number of lines zellij stores for each pane in the scroll back +// buffer. Excess number of lines are discarded in a FIFO fashion. +// Valid values: positive integers +// Default value: 10000 +// +"; + +const COPY_COMMAND_DESCRIPTION: &'static str = " +// Provide a command to execute when copying text. The text will be piped to +// the stdin of the program to perform the copy. This can be used with +// terminal emulators which do not support the OSC 52 ANSI control sequence +// that will be used by default if this option is not set. +// Examples: +// +// copy_command \"xclip -selection clipboard\" // x11 +// copy_command \"wl-copy\" // wayland +// copy_command \"pbcopy\" // osx +"; + +const COPY_CLIPBOARD_DESCRIPTION: &'static str = " +// Choose the destination for copied text +// Allows using the primary selection buffer (on x11/wayland) instead of the system clipboard. +// Does not apply when using copy_command. +// Options: +// - system (default) +// - primary +// +"; + +const COPY_ON_SELECT_DESCRIPTION: &'static str = " +// Enable or disable automatic copy (and clear) of selection when releasing mouse +// Default: true +// +"; + +const SCROLLBACK_EDITOR_DESCRIPTION: &'static str = " +// Path to the default editor to use to edit pane scrollbuffer +// Default: $EDITOR or $VISUAL +// +"; + +const MIRROR_SESSION_DESCRIPTION: &'static str = " +// When attaching to an existing session with other users, +// should the session be mirrored (true) +// or should each user have their own cursor (false) +// Default: false +// +"; + +const DEFAULT_LAYOUT_DESCRIPTION: &'static str = " +// The name of the default layout to load on startup +// Default: \"default\" +// +"; + +const LAYOUT_DIR_DESCRIPTION: &'static str = " +// The folder in which Zellij will look for layouts +// +"; + + +const THEME_DIR_DESCRIPTION: &'static str = " +// The folder in which Zellij will look for themes +// +"; + +fn options_yaml_to_options_kdl(options_yaml: &OldOptions) -> String { + let mut options_kdl = String::new(); + + macro_rules! push_option { + ($attribute_name:ident, $description_text:ident, $present_pattern:expr) => { + options_kdl.push_str($description_text); + if let Some($attribute_name) = &options_yaml.$attribute_name { + options_kdl.push_str(&format!($present_pattern, $attribute_name)); + options_kdl.push('\n'); + }; + }; + ($attribute_name:ident, $description_text:ident, $present_pattern:expr, $absent_pattern:expr) => { + options_kdl.push_str($description_text); + match &options_yaml.$attribute_name { + Some($attribute_name) => { + options_kdl.push_str(&format!($present_pattern, $attribute_name)); + }, + None => { + options_kdl.push_str(&format!($absent_pattern)); + } + }; + options_kdl.push('\n'); + } + } + + push_option!(on_force_close, ON_FORCE_CLOSE_DESCRIPTION, "on_force_close \"{}\"", "// on_force_close \"quit\""); + push_option!(simplified_ui, SIMPLIFIED_UI_DESCRIPTION, "simplified_ui {}", "// simplified_ui true"); + push_option!(default_shell, DEFAULT_SHELL_DESCRIPTION, "default_shell {:?}", "// default_shell \"fish\""); + push_option!(pane_frames, PANE_FRAMES_DESCRIPTION, "pane_frames {}", "// pane_frames true"); + push_option!(theme, DEFAULT_THEME_DESCRIPTION, "theme {:?} ", "// theme \"default\""); + push_option!(default_layout, DEFAULT_LAYOUT_DESCRIPTION, "default_layout {:?}", "// default_layout \"compact\""); + push_option!(default_mode, DEFAULT_MODE_DESCRIPTION, "default_mode \"{}\"", "// default_mode \"locked\""); + push_option!(mouse_mode, MOUSE_MODE_DESCRIPTION, "mouse_mode {}", "// mouse_mode false"); + push_option!(scroll_buffer_size, SCROLL_BUFFER_SIZE_DESCRIPTION, "scroll_buffer_size {}", "// scroll_buffer_size 10000"); + push_option!(copy_command, COPY_COMMAND_DESCRIPTION, "copy_command {:?}"); + push_option!(copy_clipboard, COPY_CLIPBOARD_DESCRIPTION, "copy_clipboard \"{}\"", "// copy_clipboard \"primary\""); + push_option!(copy_on_select, COPY_ON_SELECT_DESCRIPTION, "copy_on_select {}", "// copy_on_select false"); + push_option!(scrollback_editor, SCROLLBACK_EDITOR_DESCRIPTION, "scrollback_editor {:?}", "// scrollback_editor \"/usr/bin/vim\""); + push_option!(mirror_session, MIRROR_SESSION_DESCRIPTION, "mirror_session {}", "// mirror_session true"); + push_option!(layout_dir, LAYOUT_DIR_DESCRIPTION, "layout_dir {:?}", "// layout_dir /path/to/my/layout_dir"); + push_option!(theme_dir, THEME_DIR_DESCRIPTION, "theme_dir {:?}", "// theme_dir \"/path/to/my/theme_dir\""); + + options_kdl +} + +fn env_yaml_to_env_kdl(env_yaml: &OldEnvironmentVariablesFromYaml) -> String { + let mut env_kdl = String::new(); + let mut env_vars: Vec<(String, String)> = env_yaml.env.iter().map(|(name, val)| (name.clone(), val.clone())).collect(); + env_vars.sort_unstable(); + env_kdl.push_str("env {\n"); + for (name, val) in env_vars { + env_kdl.push_str(&format!(" {} \"{}\"\n", name, val)); + } + env_kdl.push_str("}\n"); + env_kdl +} + +fn plugins_yaml_to_plugins_kdl(plugins_yaml_to_plugins_kdl: &OldPluginsConfigFromYaml) -> String { + let mut plugins_kdl = String::new(); + if !&plugins_yaml_to_plugins_kdl.0.is_empty() { + plugins_kdl.push_str("\n"); + plugins_kdl.push_str("plugins {\n") + } + for plugin_config in &plugins_yaml_to_plugins_kdl.0 { + if plugin_config._allow_exec_host_cmd { + plugins_kdl.push_str(&format!(" {} {{ path {:?}; _allow_exec_host_cmd true; }}\n", plugin_config.tag.0, plugin_config.path)); + } else { + plugins_kdl.push_str(&format!(" {} {{ path {:?}; }}\n", plugin_config.tag.0, plugin_config.path)); + } + } + if !&plugins_yaml_to_plugins_kdl.0.is_empty() { + plugins_kdl.push_str("}\n") + } + plugins_kdl +} + +fn ui_config_yaml_to_ui_config_kdl(ui_config_yaml: &OldUiConfigFromYaml) -> String { + let mut kdl_ui_config = String::new(); + if ui_config_yaml.pane_frames.rounded_corners { + kdl_ui_config.push_str("\n"); + kdl_ui_config.push_str("ui {\n"); + kdl_ui_config.push_str(" pane_frames {\n"); + kdl_ui_config.push_str(" rounded_corners true\n"); + kdl_ui_config.push_str(" }\n"); + kdl_ui_config.push_str("}\n"); + } else { + // I'm not sure this is a thing, but since it's possible, why not? + kdl_ui_config.push_str("\n"); + kdl_ui_config.push_str("ui {\n"); + kdl_ui_config.push_str(" pane_frames {\n"); + kdl_ui_config.push_str(" rounded_corners false\n"); + kdl_ui_config.push_str(" }\n"); + kdl_ui_config.push_str("}\n"); + } + kdl_ui_config +} + +fn theme_config_yaml_to_theme_config_kdl(theme_config_yaml: &OldThemesFromYamlIntermediate) -> String { + macro_rules! theme_color { + ($theme:ident, $color:ident, $color_name:expr, $kdl_theme_config:expr) => { + match $theme.palette.$color { + OldPaletteColorFromYaml::Rgb((r, g, b)) => { + $kdl_theme_config.push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b)); + } + OldPaletteColorFromYaml::EightBit(eight_bit_color) => { + $kdl_theme_config.push_str(&format!(" {} {}\n", $color_name, eight_bit_color)); + } + OldPaletteColorFromYaml::Hex(OldHexColor(r, g, b)) => { + $kdl_theme_config.push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b)); + } + } + } + } + + let mut kdl_theme_config = String::new(); + if !theme_config_yaml.0.is_empty() { + kdl_theme_config.push_str("themes {\n") + } + let mut themes: Vec<(String, OldTheme)> = theme_config_yaml.0.iter().map(|(theme_name, theme)| (theme_name.clone(), theme.clone())).collect(); + themes.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + for (theme_name, theme) in themes { + kdl_theme_config.push_str(&format!(" {} {{\n", theme_name)); + theme_color!(theme, fg, "fg", kdl_theme_config); + theme_color!(theme, bg, "bg", kdl_theme_config); + theme_color!(theme, black, "black", kdl_theme_config); + theme_color!(theme, red, "red", kdl_theme_config); + theme_color!(theme, green, "green", kdl_theme_config); + theme_color!(theme, yellow, "yellow", kdl_theme_config); + theme_color!(theme, blue, "blue", kdl_theme_config); + theme_color!(theme, magenta, "magenta", kdl_theme_config); + theme_color!(theme, cyan, "cyan", kdl_theme_config); + theme_color!(theme, white, "white", kdl_theme_config); + theme_color!(theme, orange, "orange", kdl_theme_config); + kdl_theme_config.push_str(" }\n"); + } + if !theme_config_yaml.0.is_empty() { + kdl_theme_config.push_str("}\n") + } + kdl_theme_config +} + +fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String { + let mut kdl_keybinds = String::new(); + let modes = vec![ + // mode sort order + OldInputMode::Normal, + OldInputMode::Locked, + OldInputMode::Pane, + OldInputMode::Tab, + OldInputMode::Resize, + OldInputMode::Move, + OldInputMode::Scroll, + OldInputMode::Session, + OldInputMode::Search, + OldInputMode::EnterSearch, + OldInputMode::RenameTab, + OldInputMode::RenamePane, + OldInputMode::Prompt, + OldInputMode::Tmux, + ]; + + // title and global unbinds / clear-defaults + match &keybinds_yaml.unbind { + OldUnbind::Keys(keys_to_unbind) => { + kdl_keybinds.push_str("keybinds {\n"); + let key_string: String = keys_to_unbind.iter().map(|k| format!("\"{}\"", k)).collect::>().join(" "); + kdl_keybinds.push_str(&format!(" unbind {}\n", key_string)); + } + OldUnbind::All(should_unbind_all_defaults) => { + if *should_unbind_all_defaults { + kdl_keybinds.push_str("keybinds clear-defaults=true {\n"); + } else { + kdl_keybinds.push_str("keybinds {\n"); + } + } + } + + for mode in modes { + if let Some(mode_keybinds) = keybinds_yaml.keybinds.get(&mode) { + let mut should_clear_mode_defaults = false; + let mut kdl_mode_keybinds = String::new(); + for key_action_unbind in mode_keybinds { + match key_action_unbind { + OldKeyActionUnbind::KeyAction(key_action) => { + let keys = &key_action.key; + let actions = &key_action.action; + let key_string: String = keys.iter().map(|k| format!("\"{}\"", k)).collect::>().join(" "); + let actions_string: String = actions.iter().map(|a| format!("{};", a)).collect::>().join(" "); + kdl_mode_keybinds.push_str(&format!(" bind {} {{ {} }}\n", key_string, actions_string)); + } + OldKeyActionUnbind::Unbind(unbind) => { + match &unbind.unbind { + OldUnbind::Keys(keys_to_unbind) => { + let key_string: String = keys_to_unbind.iter().map(|k| format!("\"{}\"", k)).collect::>().join(" "); + kdl_mode_keybinds.push_str(&format!(" unbind {}\n", key_string)); + }, + OldUnbind::All(unbind_all) => { + if *unbind_all { + should_clear_mode_defaults = true; + } + } + } + } + } + } + if should_clear_mode_defaults { + kdl_keybinds.push_str(&format!(" {} clear-defaults=true {{\n", mode)); + } else { + kdl_keybinds.push_str(&format!(" {} {{\n", mode)); + } + kdl_keybinds.push_str(&kdl_mode_keybinds); + kdl_keybinds.push_str(" }\n"); + } + } + kdl_keybinds.push_str("}\n"); + kdl_keybinds +} + +pub fn config_yaml_to_config_kdl(raw_yaml_config: &str) -> Result { // returns the raw kdl config + let config_from_yaml: OldConfigFromYaml = serde_yaml::from_str(raw_yaml_config).map_err(|e| format!("Failed to parse yaml: {:?}", e))?; + let mut kdl_config = String::new(); + if let Some(old_config_keybinds) = config_from_yaml.keybinds.as_ref() { + kdl_config.push_str( + &keybinds_yaml_to_keybinds_kdl(old_config_keybinds) + ); + } + if let Some(old_config_options) = config_from_yaml.options.as_ref() { + kdl_config.push_str( + &options_yaml_to_options_kdl(old_config_options) + ); + } + if let Some(old_config_env_variables) = config_from_yaml.env.as_ref() { + kdl_config.push_str( + &env_yaml_to_env_kdl(old_config_env_variables) + ); + } + kdl_config.push_str( + &plugins_yaml_to_plugins_kdl(&config_from_yaml.plugins) + ); + if let Some(old_ui_config) = config_from_yaml.ui.as_ref() { + kdl_config.push_str( + &ui_config_yaml_to_ui_config_kdl(old_ui_config) + ); + } + if let Some(old_themes_config) = config_from_yaml.themes.as_ref() { + kdl_config.push_str( + &theme_config_yaml_to_theme_config_kdl(old_themes_config) + ); + } + Ok(kdl_config) +} + +#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq)] +struct OldConfigFromYaml { + #[serde(flatten)] + pub options: Option, + pub keybinds: Option, + pub themes: Option, + #[serde(flatten)] + pub env: Option, + #[serde(default)] + pub plugins: OldPluginsConfigFromYaml, + pub ui: Option, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldKeybindsFromYaml { + #[serde(flatten)] + keybinds: HashMap>, + #[serde(default)] + unbind: OldUnbind, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[serde(untagged)] +enum OldUnbind { + // This is the correct order, don't rearrange! + // Suspected Bug in the untagged macro. + // 1. Keys + Keys(Vec), + // 2. All + All(bool), +} + +impl Default for OldUnbind { + fn default() -> OldUnbind { + OldUnbind::All(false) + } +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(untagged)] +enum OldKeyActionUnbind { + KeyAction(OldKeyActionFromYaml), + Unbind(OldUnbindFromYaml), +} + +/// Intermediate struct used for deserialisation +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldKeyActionFromYaml { + action: Vec, + key: Vec, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldUnbindFromYaml { + unbind: OldUnbind, +} + +/// Main configuration. +#[derive(Debug, Clone, PartialEq, Deserialize)] +struct OldConfig { + pub keybinds: OldKeybinds, + pub options: OldOptions, + pub themes: Option, + pub plugins: OldPluginsConfig, + pub ui: Option, + pub env: OldEnvironmentVariablesFromYaml, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldKeybinds(HashMap); + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldModeKeybinds(BTreeMap>); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +struct OldThemesFromYamlIntermediate(HashMap); + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +struct OldPaletteFromYaml { + pub fg: OldPaletteColorFromYaml, + pub bg: OldPaletteColorFromYaml, + pub black: OldPaletteColorFromYaml, + pub red: OldPaletteColorFromYaml, + pub green: OldPaletteColorFromYaml, + pub yellow: OldPaletteColorFromYaml, + pub blue: OldPaletteColorFromYaml, + pub magenta: OldPaletteColorFromYaml, + pub cyan: OldPaletteColorFromYaml, + pub white: OldPaletteColorFromYaml, + pub orange: OldPaletteColorFromYaml, +} + +/// Intermediate deserialization enum +// This is here in order to make the untagged enum work +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(untagged)] +enum OldPaletteColorFromYaml { + Rgb((u8, u8, u8)), + EightBit(u8), + Hex(OldHexColor), +} + +impl From for (u8, u8, u8) { + fn from(e: OldHexColor) -> (u8, u8, u8) { + let OldHexColor(r, g, b) = e; + (r, g, b) + } +} + +struct OldHexColorVisitor(); + +impl<'de> Visitor<'de> for OldHexColorVisitor { + type Value = OldHexColor; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a hex color in the format #RGB or #RRGGBB") + } + + fn visit_str(self, s: &str) -> Result + where + E: Error, + { + if let Some(stripped) = s.strip_prefix('#') { + return self.visit_str(stripped); + } + + if s.len() == 3 { + Ok(OldHexColor( + u8::from_str_radix(&s[0..1], 16).map_err(E::custom)? * 0x11, + u8::from_str_radix(&s[1..2], 16).map_err(E::custom)? * 0x11, + u8::from_str_radix(&s[2..3], 16).map_err(E::custom)? * 0x11, + )) + } else if s.len() == 6 { + Ok(OldHexColor( + u8::from_str_radix(&s[0..2], 16).map_err(E::custom)?, + u8::from_str_radix(&s[2..4], 16).map_err(E::custom)?, + u8::from_str_radix(&s[4..6], 16).map_err(E::custom)?, + )) + } else { + Err(Error::custom( + "Hex color must be of form \"#RGB\" or \"#RRGGBB\"", + )) + } + } +} + +impl<'de> Deserialize<'de> for OldHexColor { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(OldHexColorVisitor()) + } +} + +impl Default for OldPaletteColorFromYaml { + fn default() -> Self { + OldPaletteColorFromYaml::EightBit(0) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +struct OldHexColor(u8, u8, u8); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +struct OldTheme { + #[serde(flatten)] + palette: OldPaletteFromYaml, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] +struct OldUiConfigFromYaml { + pub pane_frames: OldFrameConfigFromYaml, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] +struct OldFrameConfigFromYaml { + pub rounded_corners: bool, +} + +#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] +struct OldEnvironmentVariablesFromYaml { + env: HashMap, +} + +#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +struct OldPluginsConfigFromYaml(Vec); + +#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +struct OldPluginConfigFromYaml { + pub path: PathBuf, + pub tag: OldPluginTag, + #[serde(default)] + pub run: OldPluginTypeFromYaml, + #[serde(default)] + pub config: serde_yaml::Value, + #[serde(default)] + pub _allow_exec_host_cmd: bool, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +enum OldPluginTypeFromYaml { + Headless, + Pane, +} + +impl Default for OldPluginTypeFromYaml { + fn default() -> Self { + Self::Pane + } +} + +/// Tag used to identify the plugin in layout and config yaml files +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +struct OldPluginTag(String); + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldPluginsConfig(HashMap); + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +struct OldPluginConfig { + /// Path of the plugin, see resolve_wasm_bytes for resolution semantics + pub path: PathBuf, + /// Plugin type + pub run: OldPluginType, + /// Allow command execution from plugin + pub _allow_exec_host_cmd: bool, + /// Original location of the + pub location: OldRunPluginLocation, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +enum OldRunPluginLocation { + File(PathBuf), + Zellij(OldPluginTag), +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +enum OldPluginType { + /// Starts immediately when Zellij is started and runs without a visible pane + Headless, + /// Runs once per pane declared inside a layout file + Pane(Option), // tab_index +} + +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] +enum OldOnForceClose { + #[serde(alias = "quit")] + Quit, + #[serde(alias = "detach")] + Detach, +} + +impl std::fmt::Display for OldOnForceClose { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::Quit => write!(f, "quit"), + Self::Detach => write!(f, "detach"), + } + } +} + +impl Default for OldOnForceClose { + fn default() -> Self { + Self::Detach + } +} + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +enum OldClipboard { + #[serde(alias = "system")] + System, + #[serde(alias = "primary")] + Primary, +} + +impl std::fmt::Display for OldClipboard { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::System => write!(f, "system"), + Self::Primary => write!(f, "primary"), + } + } +} + +impl Default for OldClipboard { + fn default() -> Self { + Self::System + } +} + +#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize)] +struct OldOptions { + #[serde(default)] + pub simplified_ui: Option, + pub theme: Option, + pub default_mode: Option, + pub default_shell: Option, + pub default_layout: Option, + pub layout_dir: Option, + pub theme_dir: Option, + #[serde(default)] + pub mouse_mode: Option, + #[serde(default)] + pub pane_frames: Option, + #[serde(default)] + pub mirror_session: Option, + pub on_force_close: Option, + pub scroll_buffer_size: Option, + #[serde(default)] + pub copy_command: Option, + #[serde(default)] + pub copy_clipboard: Option, + #[serde(default)] + pub copy_on_select: Option, + pub scrollback_editor: Option, +} + + +/// Describes the different input modes, which change the way that keystrokes will be interpreted. +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] +enum OldInputMode { + /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading + /// to other modes + #[serde(alias = "normal")] + Normal, + /// In `Locked` mode, input is always written to the terminal and all shortcuts are disabled + /// except the one leading back to normal mode + #[serde(alias = "locked")] + Locked, + /// `Resize` mode allows resizing the different existing panes. + #[serde(alias = "resize")] + Resize, + /// `Pane` mode allows creating and closing panes, as well as moving between them. + #[serde(alias = "pane")] + Pane, + /// `Tab` mode allows creating and closing tabs, as well as moving between them. + #[serde(alias = "tab")] + Tab, + /// `Scroll` mode allows scrolling up and down within a pane. + #[serde(alias = "scroll")] + Scroll, + /// `EnterSearch` mode allows for typing in the needle for a search in the scroll buffer of a pane. + #[serde(alias = "entersearch")] + EnterSearch, + /// `Search` mode allows for searching a term in a pane (superset of `Scroll`). + #[serde(alias = "search")] + Search, + /// `RenameTab` mode allows assigning a new name to a tab. + #[serde(alias = "renametab")] + RenameTab, + /// `RenamePane` mode allows assigning a new name to a pane. + #[serde(alias = "renamepane")] + RenamePane, + /// `Session` mode allows detaching sessions + #[serde(alias = "session")] + Session, + /// `Move` mode allows moving the different existing panes within a tab + #[serde(alias = "move")] + Move, + /// `Prompt` mode allows interacting with active prompts. + #[serde(alias = "prompt")] + Prompt, + /// `Tmux` mode allows for basic tmux keybindings functionality + #[serde(alias = "tmux")] + Tmux, +} + +impl std::fmt::Display for OldInputMode { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::Normal => write!(f, "normal"), + Self::Locked => write!(f, "locked"), + Self::Resize => write!(f, "resize"), + Self::Pane => write!(f, "pane"), + Self::Tab => write!(f, "tab"), + Self::Scroll => write!(f, "scroll"), + Self::EnterSearch => write!(f, "entersearch"), + Self::Search => write!(f, "search"), + Self::RenameTab => write!(f, "RenameTab"), + Self::RenamePane => write!(f, "RenamePane"), + Self::Session => write!(f, "session"), + Self::Move => write!(f, "move"), + Self::Prompt => write!(f, "prompt"), + Self::Tmux => write!(f, "tmux"), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)] +enum OldKey { + PageDown, + PageUp, + Left, + Down, + Up, + Right, + Home, + End, + Backspace, + Delete, + Insert, + F(u8), + Char(char), + Alt(OldCharOrArrow), + Ctrl(char), + BackTab, + Null, + Esc, +} + +impl std::fmt::Display for OldKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::PageDown => write!(f, "PageDown"), + Self::PageUp => write!(f, "PageUp"), + Self::Left => write!(f, "Left"), + Self::Down => write!(f, "Down"), + Self::Up => write!(f, "Up"), + Self::Right => write!(f, "Right"), + Self::Home => write!(f, "Home"), + Self::End => write!(f, "End"), + Self::Backspace => write!(f, "Backspace"), + Self::Delete => write!(f, "Delete"), + Self::Insert => write!(f, "Insert"), + Self::F(index) => write!(f, "F{}", index), + Self::Char(c) => match c { + '\n' => write!(f, "Enter"), + '\t' => write!(f, "Tab"), + '\"' => write!(f, "\\\""), // make sure it is escaped because otherwise it will be + // seen as a KDL string starter/terminator + ' ' => write!(f, "Space"), + _ => write!(f, "{}", c), + }, + Self::Alt(char_or_arrow) => match char_or_arrow { + OldCharOrArrow::Char(c) => write!(f, "Alt {}", c), + OldCharOrArrow::Direction(direction) => { + match direction { + OldDirection::Left => write!(f, "Alt Left"), + OldDirection::Right => write!(f, "Alt Right"), + OldDirection::Up => write!(f, "Alt Up"), + OldDirection::Down => write!(f, "Alt Down"), + } + } + }, + Self::Ctrl(c) => write!(f, "Ctrl {}", c), + Self::BackTab => write!(f, "Tab"), + Self::Null => write!(f, "Null"), + Self::Esc => write!(f, "Esc"), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)] +#[serde(untagged)] +enum OldCharOrArrow { + Char(char), + Direction(OldDirection), +} + +/// The four directions (left, right, up, down). +#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)] +enum OldDirection { + Left, + Right, + Up, + Down, +} + +impl std::fmt::Display for OldDirection { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::Left => write!(f, "Left"), + Self::Right => write!(f, "Right"), + Self::Up => write!(f, "Up"), + Self::Down => write!(f, "Down"), + } + } +} + +impl Default for OldDirection { + fn default() -> Self { + OldDirection::Left + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +enum OldAction { + Quit, + Write(Vec), + WriteChars(String), + SwitchToMode(OldInputMode), + Resize(OldResizeDirection), + FocusNextPane, + FocusPreviousPane, + SwitchFocus, + MoveFocus(OldDirection), + MoveFocusOrTab(OldDirection), + MovePane(Option), + DumpScreen(String), + EditScrollback, + ScrollUp, + ScrollUpAt(OldPosition), + ScrollDown, + ScrollDownAt(OldPosition), + ScrollToBottom, + PageScrollUp, + PageScrollDown, + HalfPageScrollUp, + HalfPageScrollDown, + ToggleFocusFullscreen, + TogglePaneFrames, + ToggleActiveSyncTab, + NewPane(Option), + TogglePaneEmbedOrFloating, + ToggleFloatingPanes, + CloseFocus, + PaneNameInput(Vec), + UndoRenamePane, + NewTab(Option), + NoOp, + GoToNextTab, + GoToPreviousTab, + CloseTab, + GoToTab(u32), + ToggleTab, + TabNameInput(Vec), + UndoRenameTab, + Run(OldRunCommandAction), + Detach, + LeftClick(OldPosition), + RightClick(OldPosition), + MiddleClick(OldPosition), + LeftMouseRelease(OldPosition), + RightMouseRelease(OldPosition), + MiddleMouseRelease(OldPosition), + MouseHoldLeft(OldPosition), + MouseHoldRight(OldPosition), + MouseHoldMiddle(OldPosition), + Copy, + Confirm, + Deny, + SkipConfirm(Box), + SearchInput(Vec), + Search(OldSearchDirection), + SearchToggleOption(OldSearchOption), +} + +impl std::fmt::Display for OldAction { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::Quit => write!(f, "Quit"), + Self::Write(bytes) => write!(f, "Write {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::WriteChars(chars) => write!(f, "WriteChars \"{}\"", chars), + Self::SwitchToMode(input_mode) => write!(f, "SwitchToMode \"{}\"", input_mode), + Self::Resize(resize_direction) => write!(f, "Resize \"{}\"", resize_direction), + Self::FocusNextPane => write!(f, "FocusNextPane"), + Self::FocusPreviousPane => write!(f, "FocusPreviousPane"), + Self::SwitchFocus => write!(f, "SwitchFocus"), + Self::MoveFocus(direction) => write!(f, "MoveFocus \"{}\"", direction), + Self::MoveFocusOrTab(direction) => write!(f, "MoveFocusOrTab \"{}\"", direction), + Self::MovePane(direction) => match direction { + Some(direction) => write!(f, "MovePane \"{}\"", direction), + None => write!(f, "MovePane"), + }, + Self::DumpScreen(file) => write!(f, "DumpScreen \"{}\"", file), + Self::EditScrollback => write!(f, "EditScrollback"), + Self::ScrollUp => write!(f, "ScrollUp"), + Self::ScrollDown => write!(f, "ScrollDown"), + Self::ScrollToBottom => write!(f, "ScrollToBottom"), + Self::PageScrollUp => write!(f, "PageScrollUp"), + Self::PageScrollDown => write!(f, "PageScrollDown"), + Self::HalfPageScrollUp => write!(f, "HalfPageScrollUp"), + Self::HalfPageScrollDown => write!(f, "HalfPageScrollDown"), + Self::ToggleFocusFullscreen => write!(f, "ToggleFocusFullscreen"), + Self::TogglePaneFrames => write!(f, "TogglePaneFrames"), + Self::ToggleActiveSyncTab => write!(f, "ToggleActiveSyncTab"), + Self::NewPane(direction) => match direction { + Some(direction) => write!(f, "NewPane \"{}\"", direction), + None => write!(f, "NewPane"), + }, + Self::TogglePaneEmbedOrFloating => write!(f, "TogglePaneEmbedOrFloating"), + Self::ToggleFloatingPanes => write!(f, "ToggleFloatingPanes"), + Self::CloseFocus => write!(f, "CloseFocus"), + Self::PaneNameInput(bytes) => write!(f, "PaneNameInput {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::UndoRenamePane => write!(f, "UndoRenamePane"), + Self::NewTab(_) => write!(f, "NewTab"), + Self::NoOp => write!(f, "NoOp"), + Self::GoToNextTab => write!(f, "GoToNextTab"), + Self::GoToPreviousTab => write!(f, "GoToPreviousTab"), + Self::CloseTab => write!(f, "CloseTab"), + Self::GoToTab(index) => write!(f, "GoToTab {}", index), + Self::ToggleTab => write!(f, "ToggleTab"), + // Self::TabNameInput(bytes) => write!(f, "TabNameInput {}", format!("{}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" "))), + Self::TabNameInput(bytes) => write!(f, "TabNameInput {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::UndoRenameTab => write!(f, "UndoRenameTab"), + Self::Run(run_command_action) => { + let mut run_block_serialized = format!("Run {:?}", run_command_action.command); + for arg in &run_command_action.args { + run_block_serialized.push_str(&format!(" \"{}\"", arg)); + } + match (&run_command_action.cwd, run_command_action.direction) { + (Some(cwd), Some(direction)) => { + run_block_serialized.push_str(&format!("{{ cwd {:?}; direction \"{}\"; }}", cwd, direction)); + } + (None, Some(direction)) => { + run_block_serialized.push_str(&format!("{{ direction \"{}\"; }}", direction)); + } + (Some(cwd), None) => { + run_block_serialized.push_str(&format!("{{ cwd {:?}; }}", cwd)); + } + (None, None) => {} + } + write!(f, "{}", run_block_serialized) + } + Self::Detach => write!(f, "Detach"), + Self::Copy => write!(f, "Copy"), + Self::Confirm => write!(f, "Confirm"), + Self::Deny => write!(f, "Deny"), + Self::SearchInput(bytes) => write!(f, "SearchInput {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::Search(direction) => write!(f, "Search \"{}\"", direction), + Self::SearchToggleOption(option) => write!(f, "SearchToggleOption \"{}\"", option), + _ => Err(std::fmt::Error) + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +enum OldSearchDirection { + Down, + Up, +} + +impl std::fmt::Display for OldSearchDirection { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::Down => write!(f, "Down"), + Self::Up => write!(f, "Up"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +enum OldSearchOption { + CaseSensitivity, + WholeWord, + Wrap, +} + +impl std::fmt::Display for OldSearchOption { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::CaseSensitivity => write!(f, "CaseSensitivity"), + Self::WholeWord => write!(f, "WholeWord"), + Self::Wrap => write!(f, "Wrap"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +enum OldResizeDirection { + Left, + Right, + Up, + Down, + Increase, + Decrease, +} + +impl std::fmt::Display for OldResizeDirection { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Self::Left => write!(f, "Left"), + Self::Right => write!(f, "Right"), + Self::Up => write!(f, "Up"), + Self::Down => write!(f, "Down"), + Self::Increase => write!(f, "Increase"), + Self::Decrease => write!(f, "Decrease"), + } + } +} + +#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Deserialize, Serialize)] +struct OldPosition { + pub line: OldLine, + pub column: OldColumn, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd)] +struct OldLine(pub isize); +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd)] +struct OldColumn(pub usize); + +#[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)] +struct OldRunCommandAction { + #[serde(rename = "cmd")] + pub command: PathBuf, + #[serde(default)] + pub args: Vec, + #[serde(default)] + pub cwd: Option, + #[serde(default)] + pub direction: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +struct OldTabLayout { + #[serde(default)] + pub direction: OldDirection, + pub pane_name: Option, + #[serde(default)] + pub borderless: bool, + #[serde(default)] + pub parts: Vec, + pub split_size: Option, + #[serde(default)] + pub name: String, + pub focus: Option, + pub run: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +enum OldSplitSize { + #[serde(alias = "percent")] + Percent(u64), // 1 to 100 + #[serde(alias = "fixed")] + Fixed(usize), // An absolute number of columns or rows +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +enum OldRunFromYaml { + #[serde(rename = "plugin")] + Plugin(OldRunPluginFromYaml), + #[serde(rename = "command")] + Command(OldRunCommand), +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +struct OldRunPluginFromYaml { + #[serde(default)] + pub _allow_exec_host_cmd: bool, + pub location: Url, +} + +#[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)] +struct OldRunCommand { + #[serde(alias = "cmd")] + pub command: PathBuf, + #[serde(default)] + pub args: Vec, + #[serde(default)] + pub cwd: Option, +} + +// The unit test location. +#[path = "./unit/convert_config_tests.rs"] +#[cfg(test)] +mod convert_config_test; diff --git a/zellij-client/src/old_config_converter/old_layout.rs b/zellij-client/src/old_config_converter/old_layout.rs new file mode 100644 index 0000000000..3f0b392957 --- /dev/null +++ b/zellij-client/src/old_config_converter/old_layout.rs @@ -0,0 +1,820 @@ +//! The layout system. +// Layouts have been moved from [`zellij-server`] to +// [`zellij-utils`] in order to provide more helpful +// error messages to the user until a more general +// logging system is in place. +// In case there is a logging system in place evaluate, +// if [`zellij-utils`], or [`zellij-server`] is a proper +// place. +// If plugins should be able to depend on the layout system +// then [`zellij-utils`] could be a proper place. +use crate::{ + input::{ + command::RunCommand, + config::{ConfigError, LayoutNameInTabError}, + }, + pane_size::{Dimension, PaneGeom}, + setup, +}; + +use super::{ + config::ConfigFromYaml, + plugins::{PluginTag, PluginsConfigError}, +}; +use serde::{Deserialize, Serialize}; +use std::convert::{TryFrom, TryInto}; +use std::vec::Vec; +use std::{ + cmp::max, + fmt, fs, + ops::Not, + path::{Path, PathBuf}, +}; +use std::{fs::File, io::prelude::*}; +use url::Url; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] +pub enum Direction { + #[serde(alias = "horizontal")] + Horizontal, + #[serde(alias = "vertical")] + Vertical, +} + +impl Not for Direction { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Direction::Horizontal => Direction::Vertical, + Direction::Vertical => Direction::Horizontal, + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +pub enum SplitSize { + #[serde(alias = "percent")] + Percent(u64), // 1 to 100 + #[serde(alias = "fixed")] + Fixed(usize), // An absolute number of columns or rows +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub enum Run { + #[serde(rename = "plugin")] + Plugin(RunPlugin), + #[serde(rename = "command")] + Command(RunCommand), +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub enum RunFromYaml { + #[serde(rename = "plugin")] + Plugin(RunPluginFromYaml), + #[serde(rename = "command")] + Command(RunCommand), +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct RunPluginFromYaml { + #[serde(default)] + pub _allow_exec_host_cmd: bool, + pub location: Url, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct RunPlugin { + #[serde(default)] + pub _allow_exec_host_cmd: bool, + pub location: RunPluginLocation, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub enum RunPluginLocation { + File(PathBuf), + Zellij(PluginTag), +} + +impl From<&RunPluginLocation> for Url { + fn from(location: &RunPluginLocation) -> Self { + let url = match location { + RunPluginLocation::File(path) => format!( + "file:{}", + path.clone().into_os_string().into_string().unwrap() + ), + RunPluginLocation::Zellij(tag) => format!("zellij:{}", tag), + }; + Self::parse(&url).unwrap() + } +} + +impl fmt::Display for RunPluginLocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + Self::File(path) => write!( + f, + "{}", + path.clone().into_os_string().into_string().unwrap() + ), + + Self::Zellij(tag) => write!(f, "{}", tag), + } + } +} + +// The layout struct ultimately used to build the layouts. +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Layout { + pub direction: Direction, + #[serde(default)] + pub pane_name: Option, + #[serde(default)] + pub parts: Vec, + pub split_size: Option, + pub run: Option, + #[serde(default)] + pub borderless: bool, + pub focus: Option, +} + +// The struct that is used to deserialize the layout from +// a yaml configuration file, is needed because of: +// https://github.com/bincode-org/bincode/issues/245 +// flattened fields don't retain size information. +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(default)] +pub struct LayoutFromYamlIntermediate { + #[serde(default)] + pub template: LayoutTemplate, + #[serde(default)] + pub borderless: bool, + #[serde(default)] + pub tabs: Vec, + #[serde(default)] + pub session: SessionFromYaml, + #[serde(flatten)] + pub config: Option, +} + +// The struct that is used to deserialize the layout from +// a yaml configuration file +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] +#[serde(default)] +pub struct LayoutFromYaml { + #[serde(default)] + pub session: SessionFromYaml, + #[serde(default)] + pub template: LayoutTemplate, + #[serde(default)] + pub borderless: bool, + #[serde(default)] + pub tabs: Vec, +} + +type LayoutFromYamlIntermediateResult = Result; + +impl LayoutFromYamlIntermediate { + pub fn from_path(layout_path: &Path) -> LayoutFromYamlIntermediateResult { + let mut layout_file = File::open(&layout_path) + .or_else(|_| File::open(&layout_path.with_extension("yaml"))) + .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; + + let mut layout = String::new(); + layout_file.read_to_string(&mut layout)?; + let layout: Option = match serde_yaml::from_str(&layout) { + Err(e) => { + // needs direct check, as `[ErrorImpl]` is private + // https://github.com/dtolnay/serde-yaml/issues/121 + if layout.is_empty() { + return Ok(LayoutFromYamlIntermediate::default()); + } + return Err(ConfigError::Serde(e)); + }, + Ok(config) => config, + }; + + match layout { + Some(layout) => { + for tab in layout.tabs.clone() { + tab.check()?; + } + Ok(layout) + }, + None => Ok(LayoutFromYamlIntermediate::default()), + } + } + + pub fn from_yaml(yaml: &str) -> LayoutFromYamlIntermediateResult { + let layout: LayoutFromYamlIntermediate = match serde_yaml::from_str(yaml) { + Err(e) => { + // needs direct check, as `[ErrorImpl]` is private + // https://github.com/dtolnay/serde-yaml/issues/121 + if yaml.is_empty() { + return Ok(LayoutFromYamlIntermediate::default()); + } + return Err(ConfigError::Serde(e)); + }, + Ok(config) => config, + }; + Ok(layout) + } + + pub fn to_layout_and_config(&self) -> (LayoutFromYaml, Option) { + let config = self.config.clone(); + let layout = self.clone().into(); + (layout, config) + } + + pub fn from_path_or_default( + layout: Option<&PathBuf>, + layout_dir: Option, + ) -> Option { + layout + .map(|layout| { + // The way we determine where to look for the layout is similar to + // how a path would look for an executable. + // See the gh issue for more: https://github.com/zellij-org/zellij/issues/1412#issuecomment-1131559720 + if layout.extension().is_some() || layout.components().count() > 1 { + // We look localy! + LayoutFromYamlIntermediate::from_path(layout) + } else { + // We look in the default dir + LayoutFromYamlIntermediate::from_dir(layout, layout_dir.as_ref()) + } + }) + .or_else(|| { + Some(LayoutFromYamlIntermediate::from_dir( + &std::path::PathBuf::from("default"), + layout_dir.as_ref(), + )) + }) + } + + // It wants to use Path here, but that doesn't compile. + #[allow(clippy::ptr_arg)] + pub fn from_dir( + layout: &PathBuf, + layout_dir: Option<&PathBuf>, + ) -> LayoutFromYamlIntermediateResult { + match layout_dir { + Some(dir) => { + let layout_path = &dir.join(layout); + if layout_path.with_extension("yaml").exists() { + Self::from_path(layout_path) + } else { + LayoutFromYamlIntermediate::from_default_assets(layout) + } + }, + None => LayoutFromYamlIntermediate::from_default_assets(layout), + } + } + // Currently still needed but on nightly + // this is already possible: + // HashMap<&'static str, Vec> + pub fn from_default_assets(path: &Path) -> LayoutFromYamlIntermediateResult { + match path.to_str() { + Some("default") => Self::default_from_assets(), + Some("strider") => Self::strider_from_assets(), + Some("disable-status-bar") => Self::disable_status_from_assets(), + Some("compact") => Self::compact_from_assets(), + None | Some(_) => Err(ConfigError::IoPath( + std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), + path.into(), + )), + } + } + + // TODO Deserialize the assets from bytes &[u8], + // once serde-yaml supports zero-copy + pub fn default_from_assets() -> LayoutFromYamlIntermediateResult { + let layout: LayoutFromYamlIntermediate = + serde_yaml::from_str(&String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)?; + Ok(layout) + } + + pub fn strider_from_assets() -> LayoutFromYamlIntermediateResult { + let layout: LayoutFromYamlIntermediate = + serde_yaml::from_str(&String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)?; + Ok(layout) + } + + pub fn disable_status_from_assets() -> LayoutFromYamlIntermediateResult { + let layout: LayoutFromYamlIntermediate = + serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?; + Ok(layout) + } + + pub fn compact_from_assets() -> LayoutFromYamlIntermediateResult { + let layout: LayoutFromYamlIntermediate = + serde_yaml::from_str(&String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)?; + Ok(layout) + } +} + +type LayoutFromYamlResult = Result; + +impl LayoutFromYaml { + pub fn new(layout_path: &Path) -> LayoutFromYamlResult { + let mut layout_file = File::open(&layout_path) + .or_else(|_| File::open(&layout_path.with_extension("yaml"))) + .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; + + let mut layout = String::new(); + layout_file.read_to_string(&mut layout)?; + let layout: Option = match serde_yaml::from_str(&layout) { + Err(e) => { + // needs direct check, as `[ErrorImpl]` is private + // https://github.com/dtolnay/serde-yaml/issues/121 + if layout.is_empty() { + return Ok(LayoutFromYaml::default()); + } + return Err(ConfigError::Serde(e)); + }, + Ok(config) => config, + }; + + match layout { + Some(layout) => { + for tab in layout.tabs.clone() { + tab.check()?; + } + Ok(layout) + }, + None => Ok(LayoutFromYaml::default()), + } + } + + // It wants to use Path here, but that doesn't compile. + #[allow(clippy::ptr_arg)] + pub fn from_dir(layout: &PathBuf, layout_dir: Option<&PathBuf>) -> LayoutFromYamlResult { + match layout_dir { + Some(dir) => { + Self::new(&dir.join(layout)).or_else(|_| Self::from_default_assets(layout)) + }, + None => Self::from_default_assets(layout), + } + } + + pub fn from_path_or_default( + layout: Option<&PathBuf>, + layout_path: Option<&PathBuf>, + layout_dir: Option, + ) -> Option { + layout + .map(|p| LayoutFromYaml::from_dir(p, layout_dir.as_ref())) + .or_else(|| layout_path.map(|p| LayoutFromYaml::new(p))) + .or_else(|| { + Some(LayoutFromYaml::from_dir( + &std::path::PathBuf::from("default"), + layout_dir.as_ref(), + )) + }) + } + + // Currently still needed but on nightly + // this is already possible: + // HashMap<&'static str, Vec> + pub fn from_default_assets(path: &Path) -> LayoutFromYamlResult { + match path.to_str() { + Some("default") => Self::default_from_assets(), + Some("strider") => Self::strider_from_assets(), + Some("disable-status-bar") => Self::disable_status_from_assets(), + None | Some(_) => Err(ConfigError::IoPath( + std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), + path.into(), + )), + } + } + + // TODO Deserialize the assets from bytes &[u8], + // once serde-yaml supports zero-copy + pub fn default_from_assets() -> LayoutFromYamlResult { + let layout: LayoutFromYaml = + serde_yaml::from_str(&String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)?; + Ok(layout) + } + + pub fn strider_from_assets() -> LayoutFromYamlResult { + let layout: LayoutFromYaml = + serde_yaml::from_str(&String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)?; + Ok(layout) + } + + pub fn disable_status_from_assets() -> LayoutFromYamlResult { + let layout: LayoutFromYaml = + serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?; + Ok(layout) + } +} + +// The struct that is used to deserialize the session from +// a yaml configuration file +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +pub struct SessionFromYaml { + pub name: Option, + #[serde(default = "default_as_some_true")] + pub attach: Option, +} + +fn default_as_some_true() -> Option { + Some(true) +} + +// The struct that carries the information template that is used to +// construct the layout +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct LayoutTemplate { + pub direction: Direction, + #[serde(default)] + pub pane_name: Option, + #[serde(default)] + pub borderless: bool, + #[serde(default)] + pub parts: Vec, + #[serde(default)] + pub body: bool, + pub split_size: Option, + pub focus: Option, + pub run: Option, +} + +impl LayoutTemplate { + // Insert an optional `[TabLayout]` at the correct position + pub fn insert_tab_layout(mut self, tab_layout: Option) -> Self { + if self.body { + return tab_layout.unwrap_or_default().into(); + } + for (i, part) in self.parts.clone().iter().enumerate() { + if part.body { + self.parts.push(tab_layout.unwrap_or_default().into()); + self.parts.swap_remove(i); + break; + } + // recurse + let new_part = part.clone().insert_tab_layout(tab_layout.clone()); + self.parts.push(new_part); + self.parts.swap_remove(i); + } + self + } + + fn from_vec_tab_layout(tab_layout: Vec) -> Vec { + tab_layout + .iter() + .map(|tab_layout| Self::from(tab_layout.to_owned())) + .collect() + } +} + +// The tab-layout struct used to specify each individual tab. +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct TabLayout { + #[serde(default)] + pub direction: Direction, + pub pane_name: Option, + #[serde(default)] + pub borderless: bool, + #[serde(default)] + pub parts: Vec, + pub split_size: Option, + #[serde(default)] + pub name: String, + pub focus: Option, + pub run: Option, +} + +impl TabLayout { + fn check(&self) -> Result { + for part in &self.parts { + part.check()?; + if !part.name.is_empty() { + return Err(ConfigError::LayoutNameInTab(LayoutNameInTabError)); + } + } + Ok(self.clone()) + } +} + +impl Layout { + pub fn total_terminal_panes(&self) -> usize { + let mut total_panes = 0; + total_panes += self.parts.len(); + for part in &self.parts { + match part.run { + Some(Run::Command(_)) | None => { + total_panes += part.total_terminal_panes(); + }, + Some(Run::Plugin(_)) => {}, + } + } + total_panes + } + + pub fn total_borderless_panes(&self) -> usize { + let mut total_borderless_panes = 0; + total_borderless_panes += self.parts.iter().filter(|p| p.borderless).count(); + for part in &self.parts { + total_borderless_panes += part.total_borderless_panes(); + } + total_borderless_panes + } + pub fn extract_run_instructions(&self) -> Vec> { + let mut run_instructions = vec![]; + if self.parts.is_empty() { + run_instructions.push(self.run.clone()); + } + for part in &self.parts { + let mut current_runnables = part.extract_run_instructions(); + run_instructions.append(&mut current_runnables); + } + run_instructions + } + + pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(Layout, PaneGeom)> { + split_space(space, self) + } + + pub fn merge_layout_parts(&mut self, mut parts: Vec) { + self.parts.append(&mut parts); + } + + fn from_vec_tab_layout(tab_layout: Vec) -> Result, ConfigError> { + tab_layout + .iter() + .map(|tab_layout| Layout::try_from(tab_layout.to_owned())) + .collect() + } + + fn from_vec_template_layout( + layout_template: Vec, + ) -> Result, ConfigError> { + layout_template + .iter() + .map(|layout_template| Layout::try_from(layout_template.to_owned())) + .collect() + } +} + +fn layout_size(direction: Direction, layout: &Layout) -> usize { + fn child_layout_size( + direction: Direction, + parent_direction: Direction, + layout: &Layout, + ) -> usize { + let size = if parent_direction == direction { 1 } else { 0 }; + if layout.parts.is_empty() { + size + } else { + let children_size = layout + .parts + .iter() + .map(|p| child_layout_size(direction, layout.direction, p)) + .sum(); + max(size, children_size) + } + } + child_layout_size(direction, direction, layout) +} + +fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneGeom)> { + let mut pane_positions = Vec::new(); + let sizes: Vec> = layout.parts.iter().map(|part| part.split_size).collect(); + + let mut split_geom = Vec::new(); + let (mut current_position, split_dimension_space, mut inherited_dimension) = + match layout.direction { + Direction::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), + Direction::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), + }; + + let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); + + for (&size, part) in sizes.iter().zip(&layout.parts) { + let split_dimension = match size { + Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), + Some(SplitSize::Fixed(size)) => Dimension::fixed(size), + None => { + let free_percent = if let Some(p) = split_dimension_space.as_percent() { + p - sizes + .iter() + .map(|&s| { + if let Some(SplitSize::Percent(ip)) = s { + ip as f64 + } else { + 0.0 + } + }) + .sum::() + } else { + panic!("Implicit sizing within fixed-size panes is not supported"); + }; + Dimension::percent(free_percent / flex_parts as f64) + }, + }; + inherited_dimension.set_inner( + layout + .parts + .iter() + .map(|p| layout_size(!layout.direction, p)) + .max() + .unwrap(), + ); + let geom = match layout.direction { + Direction::Vertical => PaneGeom { + x: current_position, + y: space_to_split.y, + cols: split_dimension, + rows: inherited_dimension, + }, + Direction::Horizontal => PaneGeom { + x: space_to_split.x, + y: current_position, + cols: inherited_dimension, + rows: split_dimension, + }, + }; + split_geom.push(geom); + current_position += layout_size(layout.direction, part); + } + + for (i, part) in layout.parts.iter().enumerate() { + let part_position_and_size = split_geom.get(i).unwrap(); + if !part.parts.is_empty() { + let mut part_positions = split_space(part_position_and_size, part); + pane_positions.append(&mut part_positions); + } else { + pane_positions.push((part.clone(), *part_position_and_size)); + } + } + pane_positions +} + +impl TryFrom for RunPluginLocation { + type Error = PluginsConfigError; + + fn try_from(url: Url) -> Result { + match url.scheme() { + "zellij" => Ok(Self::Zellij(PluginTag::new(url.path()))), + "file" => { + let path = PathBuf::from(url.path()); + let canonicalize = |p: &Path| { + fs::canonicalize(p) + .map_err(|_| PluginsConfigError::InvalidPluginLocation(p.to_owned())) + }; + canonicalize(&path) + .or_else(|_| match path.strip_prefix("/") { + Ok(path) => canonicalize(path), + Err(_) => Err(PluginsConfigError::InvalidPluginLocation(path.to_owned())), + }) + .map(Self::File) + }, + _ => Err(PluginsConfigError::InvalidUrl(url)), + } + } +} + +impl TryFrom for Run { + type Error = PluginsConfigError; + + fn try_from(run: RunFromYaml) -> Result { + match run { + RunFromYaml::Command(command) => Ok(Run::Command(command)), + RunFromYaml::Plugin(plugin) => Ok(Run::Plugin(RunPlugin { + _allow_exec_host_cmd: plugin._allow_exec_host_cmd, + location: plugin.location.try_into()?, + })), + } + } +} + +impl From for LayoutFromYaml { + fn from(layout_from_yaml_intermediate: LayoutFromYamlIntermediate) -> Self { + Self { + template: layout_from_yaml_intermediate.template, + borderless: layout_from_yaml_intermediate.borderless, + tabs: layout_from_yaml_intermediate.tabs, + session: layout_from_yaml_intermediate.session, + } + } +} + +impl From for LayoutFromYamlIntermediate { + fn from(layout_from_yaml: LayoutFromYaml) -> Self { + Self { + template: layout_from_yaml.template, + borderless: layout_from_yaml.borderless, + tabs: layout_from_yaml.tabs, + config: None, + session: layout_from_yaml.session, + } + } +} + +impl Default for LayoutFromYamlIntermediate { + fn default() -> Self { + LayoutFromYaml::default().into() + } +} + +impl TryFrom for Layout { + type Error = ConfigError; + + fn try_from(tab: TabLayout) -> Result { + Ok(Layout { + direction: tab.direction, + pane_name: tab.pane_name, + borderless: tab.borderless, + parts: Self::from_vec_tab_layout(tab.parts)?, + split_size: tab.split_size, + focus: tab.focus, + run: tab.run.map(Run::try_from).transpose()?, + }) + } +} + +impl From for LayoutTemplate { + fn from(tab: TabLayout) -> Self { + Self { + direction: tab.direction, + pane_name: tab.pane_name, + borderless: tab.borderless, + parts: Self::from_vec_tab_layout(tab.parts), + body: false, + split_size: tab.split_size, + focus: tab.focus, + run: tab.run, + } + } +} + +impl TryFrom for Layout { + type Error = ConfigError; + + fn try_from(template: LayoutTemplate) -> Result { + Ok(Layout { + direction: template.direction, + pane_name: template.pane_name, + borderless: template.borderless, + parts: Self::from_vec_template_layout(template.parts)?, + split_size: template.split_size, + focus: template.focus, + run: template + .run + .map(Run::try_from) + // FIXME: This is just Result::transpose but that method is unstable, when it + // stabalizes we should swap this out. + .map_or(Ok(None), |r| r.map(Some))?, + }) + } +} + +impl Default for TabLayout { + fn default() -> Self { + Self { + direction: Direction::Horizontal, + borderless: false, + parts: vec![], + split_size: None, + run: None, + name: String::new(), + pane_name: None, + focus: None, + } + } +} + +impl Default for LayoutTemplate { + fn default() -> Self { + Self { + direction: Direction::Horizontal, + pane_name: None, + body: false, + borderless: false, + parts: vec![LayoutTemplate { + direction: Direction::Horizontal, + pane_name: None, + body: true, + borderless: false, + split_size: None, + focus: None, + run: None, + parts: vec![], + }], + split_size: None, + focus: None, + run: None, + } + } +} + +impl Default for Direction { + fn default() -> Self { + Direction::Horizontal + } +} + +// The unit test location. +#[cfg(test)] +#[path = "./unit/layout_test.rs"] +mod layout_test; diff --git a/zellij-client/src/old_config_converter/unit/convert_config_tests.rs b/zellij-client/src/old_config_converter/unit/convert_config_tests.rs new file mode 100644 index 0000000000..0b93cf7a89 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/convert_config_tests.rs @@ -0,0 +1,92 @@ +use crate::old_config_converter::config_yaml_to_config_kdl; +use std::path::PathBuf; +use std::{fs::File, io::prelude::*}; +use insta::assert_snapshot; + +#[test] +fn properly_convert_default_config() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_custom_options() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_keybind_unbinds_in_mode() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_global_keybind_unbinds() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_unbind_all_keys_per_mode() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_env_variables() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_ui_config() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn convert_config_with_themes_config() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml new file mode 100644 index 0000000000..cf0e5150d7 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml @@ -0,0 +1,639 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml new file mode 100644 index 0000000000..797a3f2249 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml @@ -0,0 +1,639 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml new file mode 100644 index 0000000000..26006ba384 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml @@ -0,0 +1,644 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +env: + foo: bar + bar: baz + RUST_BACKTRACE: "1" + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml new file mode 100644 index 0000000000..819764cb51 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml @@ -0,0 +1,639 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: [Ctrl: 'g', Char: ' '] + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml new file mode 100644 index 0000000000..d2839a64cb --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml @@ -0,0 +1,677 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano +# +themes: + nord: + fg: "D8DEE9" + bg: "#2E3440" + black: "#3B4252" + red: "#BF616A" + green: "#A3BE8C" + yellow: "#EBCB8B" + blue: "#81A1C1" + magenta: "#B48EAD" + cyan: "#88C0D0" + white: "#E5E9F0" + orange: "#D08770" + molokai-dark: + bg: [27, 29, 30] + red: [255, 0, 0] + green: [0, 140, 0] + yellow: [255, 255, 0] + blue: [102, 217, 239] + magenta: [174, 129, 255] + orange: [253, 151, 31] + fg: [248, 248, 240] + cyan: [0, 255, 255] + black: [0, 0, 0] + white: [255, 255, 255] + some-eightbit-theme: + bg: 0 + red: 2 + green: 3 + yellow: 4 + blue: 5 + magenta: 6 + orange: 7 + fg: 8 + cyan: 9 + black: 10 + white: 255 diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml new file mode 100644 index 0000000000..edf4af81c3 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml @@ -0,0 +1,643 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +ui: + pane_frames: + rounded_corners: true +# Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml new file mode 100644 index 0000000000..5cf962b807 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml @@ -0,0 +1,639 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + normal: + - unbind: true + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml new file mode 100644 index 0000000000..fff9a52854 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml @@ -0,0 +1,640 @@ +--- +# Configuration for zellij. + +# In order to troubleshoot your configuration try using the following command: +# `zellij setup --check` +# It should show current config locations and features that are enabled. + +keybinds: + unbind: true + normal: + - unbind: [Ctrl: 'a', Char: "\n"] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right ] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up, ] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'n', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [Resize: Increase,] + key: [Char: '='] + - action: [Resize: Increase,] + key: [ Char: '+'] + - action: [Resize: Decrease,] + key: [Char: '-'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: , SwitchToMode: Normal,] + key: [Char: 'n',] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: 'd',] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: 'r',] + - action: [CloseFocus, SwitchToMode: Normal,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'f',] + - action: [TogglePaneFrames, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [ToggleFloatingPanes, SwitchToMode: Normal,] + key: [Char: 'w'] + - action: [TogglePaneEmbedOrFloating, SwitchToMode: Normal,] + key: [Char: 'e'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] + move: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'h', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [MovePane: ,] + key: [Char: 'n', Char: "\t",] + - action: [MovePane: Left,] + key: [Char: 'h', Left,] + - action: [MovePane: Down,] + key: [Char: 'j', Down,] + - action: [MovePane: Up,] + key: [Char: 'k', Up, ] + - action: [MovePane: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 't', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [GoToPreviousTab,] + key: [ Char: 'h', Left, Up, Char: 'k',] + - action: [GoToNextTab,] + key: [ Char: 'l', Right,Down, Char: 'j'] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'n',] + - action: [CloseTab, SwitchToMode: Normal,] + key: [ Char: 'x',] + - action: [ToggleActiveSyncTab, SwitchToMode: Normal,] + key: [Char: 's'] + - action: [GoToTab: 1, SwitchToMode: Normal,] + key: [ Char: '1',] + - action: [GoToTab: 2, SwitchToMode: Normal,] + key: [ Char: '2',] + - action: [GoToTab: 3, SwitchToMode: Normal,] + key: [ Char: '3',] + - action: [GoToTab: 4, SwitchToMode: Normal,] + key: [ Char: '4',] + - action: [GoToTab: 5, SwitchToMode: Normal,] + key: [ Char: '5',] + - action: [GoToTab: 6, SwitchToMode: Normal,] + key: [ Char: '6',] + - action: [GoToTab: 7, SwitchToMode: Normal,] + key: [ Char: '7',] + - action: [GoToTab: 8, SwitchToMode: Normal,] + key: [ Char: '8',] + - action: [GoToTab: 9, SwitchToMode: Normal,] + key: [ Char: '9',] + - action: [ToggleTab] + key: [ Char: "\t" ] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + scroll: + - action: [EditScrollback, SwitchToMode: Normal] + key: [Char: 'e'] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + entersearch: + - action: [SwitchToMode: Search,] + key: [Char: "\n"] + - action: [SearchInput: [27], SwitchToMode: Scroll,] + key: [Ctrl: 'c', Esc] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + search: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 's', Char: ' ', Char: "\n", Esc] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Session,] + key: [Ctrl: 'o',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown, Right, Char: 'l',] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp, Left, Char: 'h',] + - action: [HalfPageScrollDown,] + key: [Char: 'd',] + - action: [HalfPageScrollUp,] + key: [Char: 'u',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] # The Alt: Left etc. variants are temporary hacks and will be removed in the future - please do not rely on them! + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [SwitchToMode: EnterSearch, SearchInput: [0],] + key: [Char: 's'] + - action: [Search: Down] + key: [Char: 'n'] + - action: [Search: Up] + key: [Char: 'p'] + - action: [SearchToggleOption: CaseSensitivity] + key: [Char: 'c'] + - action: [SearchToggleOption: Wrap] + key: [Char: 'w'] + - action: [SearchToggleOption: WholeWord] + key: [Char: 'o'] + renametab: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenameTab , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Char: "\n", Ctrl: 'c', Esc] + - action: [UndoRenamePane , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + session: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tmux,] + key: [Ctrl: 'b',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Detach,] + key: [Char: 'd',] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + tmux: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Move,] + key: [Ctrl: 'h',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'o', Char: "\n", Char: ' ', Esc] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: Scroll,] + key: [ Char: '['] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [Write: [2,], SwitchToMode: Normal] + key: [Ctrl: 'b'] + - action: [NewPane: Down, SwitchToMode: Normal,] + key: [Char: "\"",] + - action: [NewPane: Right, SwitchToMode: Normal,] + key: [Char: '%',] + - action: [ToggleFocusFullscreen, SwitchToMode: Normal,] + key: [Char: 'z',] + - action: [NewTab: , SwitchToMode: Normal,] + key: [ Char: 'c',] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: ','] + - action: [GoToPreviousTab, SwitchToMode: Normal,] + key: [ Char: 'p'] + - action: [GoToNextTab, SwitchToMode: Normal,] + key: [ Char: 'n'] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Left,] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Right,] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Down,] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Up,] + - action: [MoveFocus: Left, SwitchToMode: Normal,] + key: [ Char: 'h'] + - action: [MoveFocus: Right, SwitchToMode: Normal,] + key: [ Char: 'l'] + - action: [MoveFocus: Down, SwitchToMode: Normal,] + key: [ Char: 'j'] + - action: [MoveFocus: Up, SwitchToMode: Normal,] + key: [ Char: 'k'] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocusOrTab: Left,] + key: [ Alt: 'h', Alt: Left] + - action: [MoveFocusOrTab: Right,] + key: [ Alt: 'l', Alt: Right] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Alt: Down] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Alt: Up] + - action: [FocusNextPane,] + key: [ Char: 'o'] + - action: [Resize: Increase,] + key: [ Alt: '='] + - action: [Resize: Increase,] + key: [ Alt: '+'] + - action: [Resize: Decrease,] + key: [ Alt: '-'] + - action: [Detach,] + key: [Char: 'd',] +plugins: + - path: tab-bar + tag: tab-bar + - path: status-bar + tag: status-bar + - path: strider + tag: strider + - path: compact-bar + tag: compact-bar + +# 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 + +# Send a request for a simplified ui (without arrow fonts) to plugins +# Options: +# - true +# - false (Default) +# simplified_ui: true + +# Choose the path to the default shell that zellij will use for opening new panes +# Default: $SHELL +# default_shell: fish + +# Toggle between having pane frames around the panes +# Options: +# - true (default) +# - false +#pane_frames: true + +# Choose the theme that is specified in the themes section. +# For some examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes +# Default: default +#theme: default + +# Choose the mode that zellij uses when starting up. +# Default: normal +#default_mode: locked + +# Toggle enabling the mouse mode. +# On certain configurations, or terminals this could +# potentially interfere with copying text. +# Options: +# - true (default) +# - false +#mouse_mode: false + +# 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 + +# 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 or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true + +# Path to the default editor to use to edit pane scrollbuffer +# scrollback_editor: /usr/bin/nano diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_custom_options.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_custom_options.snap new file mode 100644 index 0000000000..34539e60f0 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_custom_options.snap @@ -0,0 +1,411 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 24 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +mouse_mode false + +// 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 or disable automatic copy (and clear) 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/nano" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_env_variables.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_env_variables.snap new file mode 100644 index 0000000000..c28f4afa28 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_env_variables.snap @@ -0,0 +1,415 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 68 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" +env { + RUST_BACKTRACE "1" + bar "baz" + foo "bar" +} + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_global_keybind_unbinds.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_global_keybind_unbinds.snap new file mode 100644 index 0000000000..5ce03e2496 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_global_keybind_unbinds.snap @@ -0,0 +1,411 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 46 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds { + unbind "Ctrl g" "Space" + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_keybind_unbinds_in_mode.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_keybind_unbinds_in_mode.snap new file mode 100644 index 0000000000..d7ac647436 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_keybind_unbinds_in_mode.snap @@ -0,0 +1,411 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 35 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds clear-defaults=true { + normal { + unbind "Ctrl a" "Enter" + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_themes_config.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_themes_config.snap new file mode 100644 index 0000000000..f8a65133b9 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_themes_config.snap @@ -0,0 +1,451 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 90 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} +themes { + molokai-dark { + fg 248 248 240 + bg 27 29 30 + black 0 0 0 + red 255 0 0 + green 0 140 0 + yellow 255 255 0 + blue 102 217 239 + magenta 174 129 255 + cyan 0 255 255 + white 255 255 255 + orange 253 151 31 + } + nord { + fg 216 222 233 + bg 46 52 64 + black 59 66 82 + red 191 97 106 + green 163 190 140 + yellow 235 203 139 + blue 129 161 193 + magenta 180 142 173 + cyan 136 192 208 + white 229 233 240 + orange 208 135 112 + } + some-eightbit-theme { + fg 8 + bg 0 + black 10 + red 2 + green 3 + yellow 4 + blue 5 + magenta 6 + cyan 9 + white 255 + orange 7 + } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_ui_config.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_ui_config.snap new file mode 100644 index 0000000000..e805563a37 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_ui_config.snap @@ -0,0 +1,416 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 79 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + +ui { + pane_frames { + rounded_corners true + } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_unbind_all_keys_per_mode.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_unbind_all_keys_per_mode.snap new file mode 100644 index 0000000000..2b35143e5b --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_unbind_all_keys_per_mode.snap @@ -0,0 +1,410 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 57 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds { + normal clear-defaults=true { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__properly_convert_default_config.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__properly_convert_default_config.snap new file mode 100644 index 0000000000..461258e4c1 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__properly_convert_default_config.snap @@ -0,0 +1,410 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_config_tests.rs +assertion_line: 13 +expression: "format!(\"{}\", kdl_config)" +--- +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + } + pane { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl p" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "p" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "normal"; } + bind "d" { NewPane "Down"; SwitchToMode "normal"; } + bind "r" { NewPane "Right"; SwitchToMode "normal"; } + bind "x" { CloseFocus; SwitchToMode "normal"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "z" { TogglePaneFrames; SwitchToMode "normal"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0; } + } + tab { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "Ctrl q" { Quit; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "normal"; } + bind "x" { CloseTab; SwitchToMode "normal"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } + 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 "Tab" { ToggleTab; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + resize { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl q" { Quit; } + bind "h" "Left" { Resize "Left"; } + bind "j" "Down" { Resize "Down"; } + bind "k" "Up" { Resize "Up"; } + bind "l" "Right" { Resize "Right"; } + bind "=" { Resize "Increase"; } + bind "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + move { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl h" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl q" { Quit; } + bind "n" "Tab" { MovePane; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + scroll { + bind "e" { EditScrollback; SwitchToMode "normal"; } + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + } + session { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "d" { Detach; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + search { + bind "Ctrl s" "Space" "Enter" "Esc" { SwitchToMode "normal"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl b" { SwitchToMode "tmux"; } + bind "Ctrl o" { SwitchToMode "session"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } + bind "Ctrl q" { Quit; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "s" { SwitchToMode "entersearch"; SearchInput 0; } + bind "n" { Search "Down"; } + bind "p" { Search "Up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Enter" { SwitchToMode "search"; } + bind "Ctrl c" "Esc" { SearchInput 27; SwitchToMode "scroll"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenameTab { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenameTab; SwitchToMode "tab"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + RenamePane { + bind "Enter" "Ctrl c" "Esc" { SwitchToMode "normal"; } + bind "Esc" { UndoRenamePane; SwitchToMode "pane"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + } + tmux { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl h" { SwitchToMode "move"; } + bind "Ctrl t" { SwitchToMode "tab"; } + bind "Ctrl o" "Enter" "Space" "Esc" { SwitchToMode "normal"; } + bind "Ctrl s" { SwitchToMode "scroll"; } + bind "[" { SwitchToMode "scroll"; } + bind "Ctrl q" { Quit; } + bind "Ctrl b" { Write 2; SwitchToMode "normal"; } + bind "\"" { NewPane "Down"; SwitchToMode "normal"; } + bind "%" { NewPane "Right"; SwitchToMode "normal"; } + bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } + bind "c" { NewTab; SwitchToMode "normal"; } + bind "," { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "p" { GoToPreviousTab; SwitchToMode "normal"; } + bind "n" { GoToNextTab; SwitchToMode "normal"; } + bind "Left" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "Right" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "Down" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "Up" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "h" { MoveFocus "Left"; SwitchToMode "normal"; } + bind "l" { MoveFocus "Right"; SwitchToMode "normal"; } + bind "j" { MoveFocus "Down"; SwitchToMode "normal"; } + bind "k" { MoveFocus "Up"; SwitchToMode "normal"; } + bind "Alt n" { NewPane; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "o" { FocusNextPane; } + bind "Alt =" { Resize "Increase"; } + bind "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "d" { Detach; } + } +} + +// 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" + +// Send a request for a simplified ui (without arrow fonts) to plugins +// Options: +// - true +// - false (Default) +// +// simplified_ui true + +// Choose the path to the default shell that zellij will use for opening new panes +// Default: $SHELL +// +// default_shell "fish" + +// Toggle between having pane frames around the panes +// Options: +// - true (default) +// - false +// +// pane_frames true + +// Choose the theme that is specified in the themes section. +// Default: default +// +// theme "default" + +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + +// Choose the mode that zellij uses when starting up. +// Default: normal +// +// default_mode "locked" + +// Toggle enabling the mouse mode. +// On certain configurations, or terminals this could +// potentially interfere with copying text. +// Options: +// - true (default) +// - false +// +// mouse_mode false + +// 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 + +// 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 or disable automatic copy (and clear) 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 "/usr/bin/vim" + +// 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 + +// The folder in which Zellij will look for layouts +// +// layout_dir /path/to/my/layout_dir + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" + +plugins { + tab-bar { path "tab-bar"; } + status-bar { path "status-bar"; } + strider { path "strider"; } + compact-bar { path "compact-bar"; } +} + diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 02141925d6..227dea433d 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -222,6 +222,11 @@ plugins { // // theme "default" +// The name of the default layout to load on startup +// Default: "default" +// +// default_layout "compact" + // Choose the mode that zellij uses when starting up. // Default: normal // @@ -279,3 +284,11 @@ plugins { // Default: false // // mirror_session true + +// The folder in which Zellij will look for layouts +// +// layout_dir "/path/to/my/layout_dir" + +// The folder in which Zellij will look for themes +// +// theme_dir "/path/to/my/theme_dir" diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index d2cfe60342..7b72432518 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -136,6 +136,11 @@ pub enum Sessions { args: Option, #[clap(short, long, value_parser, default_missing_value("true"))] floating: Option, + }, + #[clap(visible_alias = "co")] + Convert { + old_config_file: PathBuf, + output: Option, } } diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 5af1aa22b1..6ce9e26267 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -350,8 +350,6 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor { // eg. #fff (hex, will be converted to rgb) let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); s.remove(0); - // TODO: test this - // TODO: why do we need the * 0x11 here? let r = u8::from_str_radix(&s[0..1], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; let g = u8::from_str_radix(&s[1..2], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; let b = u8::from_str_radix(&s[2..3], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; @@ -366,7 +364,7 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor { Ok(PaletteColor::Rgb((r, g, b))) } else if is_eight_bit() { let n = kdl_first_entry_as_i64!(color).ok_or(format!("Failed to parse color"))?; - Ok(PaletteColor::EightBit(n as u8)) // TODO: test values greater than u8 bounds + Ok(PaletteColor::EightBit(n as u8)) } else { Err("Failed to parse color".into()) } @@ -421,7 +419,7 @@ impl TryFrom<&KdlNode> for Action { "DumpScreen" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "NewPane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), "PaneNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), - "NewTab" => Ok(Action::NewTab(None, None)), // TODO: consider the Some(TabLayout, "tab_name") case... + "NewTab" => Ok(Action::NewTab(None, None)), "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), "SearchInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), @@ -446,6 +444,7 @@ impl TryFrom<&KdlNode> for Action { cwd, direction, }; + log::info!("run_command_action: {:?}", run_command_action); Ok(Action::Run(run_command_action)) } _ => { @@ -778,8 +777,6 @@ impl EnvironmentVariables { impl Keybinds { fn bind_keys_in_block(block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError> { let all_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode"); -// let bind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "bind"); -// let unbind_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode").iter().filter(|n| kdl_name!(n) == "unbind"); let bind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "bind"); let unbind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "unbind"); for key_block in bind_nodes { From 725ec70e0c2404b8ec62e4c764b22ce02409ecf0 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 22 Sep 2022 16:22:55 +0200 Subject: [PATCH 37/55] feat(converter): convert old layout and theme files --- example/themes/README.md | 4 +- example/themes/dracula.kdl | 17 + example/themes/dracula.yaml | 16 - example/themes/gruvbox-dark.kdl | 16 + example/themes/gruvbox-dark.yaml | 15 - example/themes/gruvbox-light.kdl | 16 + example/themes/gruvbox-light.yaml | 15 - example/themes/molokai-dark.kdl | 16 + example/themes/molokai-dark.yaml | 15 - example/themes/nord.kdl | 15 + example/themes/nord.yaml | 16 - example/themes/one-half-dark.kdl | 16 + example/themes/one-half-dark.yaml | 15 - example/themes/solarized-dark.kdl | 16 + example/themes/solarized-dark.yaml | 15 - example/themes/solarized-light.kdl | 16 + example/themes/solarized-light.yaml | 15 - example/themes/tokyo-night-dark.kdl | 0 example/themes/tokyo-night-light.kdl | 16 + example/themes/tokyo-night-light.yaml | 16 - example/themes/tokyo-night-storm.kdl | 16 + example/themes/tokyo-night-storm.yaml | 16 - example/themes/tokyo-night.kdl | 16 + example/themes/tokyo-night.yaml | 16 - src/commands.rs | 54 +- src/main.rs | 10 +- zellij-client/src/old_config_converter/mod.rs | 2 + .../src/old_config_converter/old_config.rs | 47 +- .../src/old_config_converter/old_layout.rs | 948 +++++------------- .../unit/convert_config_tests.rs | 16 +- .../unit/convert_layout_tests.rs | 103 ++ .../unit/fixtures/multiple_tabs_layout.yaml | 90 ++ .../multiple_tabs_layout_htop_command.yaml | 93 ++ .../fixtures/old_default_yaml_layout.yaml | 22 + .../fixtures/old_yaml_layout_with_config.yaml | 34 + ...l_layout_with_config_and_session_name.yaml | 36 + .../old_yaml_layout_with_session_name.yaml | 24 + ...ut_with_session_name_and_attach_false.yaml | 25 + .../unit/fixtures/run_htop_layout.yaml | 21 + .../run_htop_layout_with_plugins.yaml | 35 + ...test__properly_convert_default_layout.snap | 14 + ...st__properly_convert_layout_example_1.snap | 61 ++ ...st__properly_convert_layout_example_2.snap | 60 ++ ...st__properly_convert_layout_example_3.snap | 19 + ...st__properly_convert_layout_example_4.snap | 27 + ...__properly_convert_layout_with_config.snap | 24 + ...t_layout_with_config_and_session_name.snap | 26 + ...erly_convert_layout_with_session_name.snap | 16 + ...ut_with_session_name_and_attach_false.snap | 16 + zellij-utils/src/cli.rs | 13 +- zellij-utils/src/input/unit/layout_test.rs | 2 +- ...est__layout_with_default_tab_template.snap | 99 +- zellij-utils/src/kdl/kdl_layout_parser.rs | 10 +- 53 files changed, 1411 insertions(+), 886 deletions(-) create mode 100644 example/themes/dracula.kdl delete mode 100644 example/themes/dracula.yaml create mode 100644 example/themes/gruvbox-dark.kdl delete mode 100644 example/themes/gruvbox-dark.yaml create mode 100644 example/themes/gruvbox-light.kdl delete mode 100644 example/themes/gruvbox-light.yaml create mode 100644 example/themes/molokai-dark.kdl delete mode 100644 example/themes/molokai-dark.yaml create mode 100644 example/themes/nord.kdl delete mode 100644 example/themes/nord.yaml create mode 100644 example/themes/one-half-dark.kdl delete mode 100644 example/themes/one-half-dark.yaml create mode 100644 example/themes/solarized-dark.kdl delete mode 100644 example/themes/solarized-dark.yaml create mode 100644 example/themes/solarized-light.kdl delete mode 100644 example/themes/solarized-light.yaml create mode 100644 example/themes/tokyo-night-dark.kdl create mode 100644 example/themes/tokyo-night-light.kdl delete mode 100644 example/themes/tokyo-night-light.yaml create mode 100644 example/themes/tokyo-night-storm.kdl delete mode 100644 example/themes/tokyo-night-storm.yaml create mode 100644 example/themes/tokyo-night.kdl delete mode 100644 example/themes/tokyo-night.yaml create mode 100644 zellij-client/src/old_config_converter/unit/convert_layout_tests.rs create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml create mode 100644 zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_default_layout.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_1.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_2.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_3.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_4.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config_and_session_name.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name.snap create mode 100644 zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name_and_attach_false.snap diff --git a/example/themes/README.md b/example/themes/README.md index ade4d6cd6f..8572222a56 100644 --- a/example/themes/README.md +++ b/example/themes/README.md @@ -1,7 +1,7 @@ # Themes -Please make sure that the theme name and the file name are the same (+`.yaml`). +Please make sure that the theme name and the file name are the same (+`.kdl`). Example: - theme: gruvbox -- filename: `gruvbox.yaml` +- filename: `gruvbox.kdl` diff --git a/example/themes/dracula.kdl b/example/themes/dracula.kdl new file mode 100644 index 0000000000..26f20504eb --- /dev/null +++ b/example/themes/dracula.kdl @@ -0,0 +1,17 @@ +// From https://github.com/dracula/zellij + +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 + } +} diff --git a/example/themes/dracula.yaml b/example/themes/dracula.yaml deleted file mode 100644 index b9c8a5afb7..0000000000 --- a/example/themes/dracula.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Dracula Theme - -themes: - dracula: - # From https://github.com/dracula/zellij - 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] - fg: [248, 248, 242] - cyan: [139, 233, 253] - black: [0, 0, 0] - white: [255, 255, 255] diff --git a/example/themes/gruvbox-dark.kdl b/example/themes/gruvbox-dark.kdl new file mode 100644 index 0000000000..53c9d6ab3c --- /dev/null +++ b/example/themes/gruvbox-dark.kdl @@ -0,0 +1,16 @@ +themes { + gruvbox-dark { + fg 213 196 161 + bg 40 40 40 + black 60 56 54 + red 204 36 29 + green 152 151 26 + yellow 215 153 33 + blue 69 133 136 + magenta 177 98 134 + cyan 104 157 106 + white 251 241 199 + orange 214 93 14 + } +} + diff --git a/example/themes/gruvbox-dark.yaml b/example/themes/gruvbox-dark.yaml deleted file mode 100644 index 676bc42b97..0000000000 --- a/example/themes/gruvbox-dark.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Gruvbox theme - -themes: - gruvbox-dark: - bg: [40, 40, 40] - red: [204, 36, 29] - green: [152, 151, 26] - yellow: [215, 153, 33] - blue: [69, 133, 136] - magenta: [177, 98, 134] - orange: [214, 93, 14] - fg: [213, 196, 161] - cyan: [104, 157, 106] - black: [60, 56, 54] - white: [251, 241, 199] diff --git a/example/themes/gruvbox-light.kdl b/example/themes/gruvbox-light.kdl new file mode 100644 index 0000000000..7bb33e6830 --- /dev/null +++ b/example/themes/gruvbox-light.kdl @@ -0,0 +1,16 @@ +themes { + gruvbox-light { + fg 60 56 54 + bg 251 82 75 + black 40 40 40 + red 205 75 69 + green 152 151 26 + yellow 215 153 33 + blue 69 133 136 + magenta 177 98 134 + cyan 104 157 106 + white 213 196 161 + orange 214 93 14 + } +} + diff --git a/example/themes/gruvbox-light.yaml b/example/themes/gruvbox-light.yaml deleted file mode 100644 index d672ec3b82..0000000000 --- a/example/themes/gruvbox-light.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Gruvbox theme - -themes: - gruvbox-light: - bg: [251, 82, 75] - red: [205, 75, 69] - green: [152, 151, 26] - yellow: [215, 153, 33] - blue: [69, 133, 136] - magenta: [177, 98, 134] - orange: [214, 93, 14] - fg: [60, 56, 54] - cyan: [104, 157, 106] - black: [40, 40, 40] - white: [213, 196, 161] diff --git a/example/themes/molokai-dark.kdl b/example/themes/molokai-dark.kdl new file mode 100644 index 0000000000..6fd96f9afe --- /dev/null +++ b/example/themes/molokai-dark.kdl @@ -0,0 +1,16 @@ +themes { + molokai-dark { + fg 248 248 240 + bg 27 29 30 + black 0 0 0 + red 255 0 0 + green 0 140 0 + yellow 255 255 0 + blue 102 217 239 + magenta 174 129 255 + cyan 0 255 255 + white 255 255 255 + orange 253 151 31 + } +} + diff --git a/example/themes/molokai-dark.yaml b/example/themes/molokai-dark.yaml deleted file mode 100644 index b2135fdfc2..0000000000 --- a/example/themes/molokai-dark.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Molokai Theme - -themes: - molokai-dark: - bg: [27, 29, 30] - red: [255, 0, 0] - green: [0, 140, 0] - yellow: [255, 255, 0] - blue: [102, 217, 239] - magenta: [174, 129, 255] - orange: [253, 151, 31] - fg: [248, 248, 240] - cyan: [0, 255, 255] - black: [0, 0, 0] - white: [255, 255, 255] diff --git a/example/themes/nord.kdl b/example/themes/nord.kdl new file mode 100644 index 0000000000..a56a9bfb39 --- /dev/null +++ b/example/themes/nord.kdl @@ -0,0 +1,15 @@ +themes { + nord { + fg 216 222 233 // #D8DEE9 + bg 46 52 64 // #2E3440 + black 59 66 82 // #3B4252 + red 191 97 106 // #BF616A + green 163 190 140 // #A3BE8C + yellow 235 203 139 // #EBCB8B + blue 129 161 193 // #81A1C1 + magenta 180 142 173 // #B48EAD + cyan 136 192 208 // #88C0D0 + white 229 233 240 // #E5E9F0 + orange 208 135 112 // #D08770 + } +} diff --git a/example/themes/nord.yaml b/example/themes/nord.yaml deleted file mode 100644 index 61851ad201..0000000000 --- a/example/themes/nord.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Nord theme - -themes: - nord: - fg: [216, 222, 233] #D8DEE9 - bg: [46, 52, 64] #2E3440 - black: [59, 66, 82] #3B4252 - red: [191, 97, 106] #BF616A - green: [163, 190, 140] #A3BE8C - yellow: [235,203,139] #EBCB8B - blue: [129, 161, 193] #81A1C1 - magenta: [180, 142, 173] #B48EAD - cyan: [136, 192, 208] #88C0D0 - white: [229, 233, 240] #E5E9F0 - orange: [208, 135, 112] #D08770 - diff --git a/example/themes/one-half-dark.kdl b/example/themes/one-half-dark.kdl new file mode 100644 index 0000000000..ade1874bf0 --- /dev/null +++ b/example/themes/one-half-dark.kdl @@ -0,0 +1,16 @@ +themes { + one-half-dark { + fg 220 223 228 + bg 40 44 52 + black 27 29 35 + red 227 63 76 + green 152 195 121 + yellow 229 192 123 + blue 97 175 239 + magenta 198 120 221 + cyan 86 182 194 + white 233 225 254 + orange 216 133 76 + } +} + diff --git a/example/themes/one-half-dark.yaml b/example/themes/one-half-dark.yaml deleted file mode 100644 index 5f3b3968a4..0000000000 --- a/example/themes/one-half-dark.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# One Half Theme - -themes: - one-half-dark: - bg: [40, 44, 52] - red: [227, 63, 76] - green: [152, 195, 121] - yellow: [229, 192, 123] - blue: [97, 175, 239] - magenta: [198, 120, 221] - orange: [216, 133, 76] - fg: [220, 223, 228] - cyan: [86, 182, 194] - black: [27, 29, 35] - white: [233, 225, 254] \ No newline at end of file diff --git a/example/themes/solarized-dark.kdl b/example/themes/solarized-dark.kdl new file mode 100644 index 0000000000..20482f5c33 --- /dev/null +++ b/example/themes/solarized-dark.kdl @@ -0,0 +1,16 @@ +themes { + solarized-dark { + fg 253 246 227 + bg 0 43 54 + black 7 54 66 + red 220 50 47 + green 133 153 0 + yellow 181 137 0 + blue 38 139 210 + magenta 211 54 130 + cyan 42 161 152 + white 238 232 213 + orange 203 75 22 + } +} + diff --git a/example/themes/solarized-dark.yaml b/example/themes/solarized-dark.yaml deleted file mode 100644 index c93635e47f..0000000000 --- a/example/themes/solarized-dark.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Solarized dark - -themes: - solarized-dark: - bg: [0, 43, 54] - red: [220, 50, 47] - green: [133, 153, 0] - yellow: [181, 137, 0] - blue: [38, 139, 210] - magenta: [211, 54, 130] - orange: [203, 75, 22] - fg: [253, 246, 227] - cyan: [42, 161, 152] - black: [7, 54, 66] - white: [238, 232, 213] diff --git a/example/themes/solarized-light.kdl b/example/themes/solarized-light.kdl new file mode 100644 index 0000000000..93bac49b7b --- /dev/null +++ b/example/themes/solarized-light.kdl @@ -0,0 +1,16 @@ +themes { + solarized-light { + fg 101 123 131 + bg 253 246 227 + black 7 54 66 + red 220 50 47 + green 133 153 0 + yellow 181 137 0 + blue 38 139 210 + magenta 211 54 130 + cyan 42 161 152 + white 238 232 213 + orange 203 75 22 + } +} + diff --git a/example/themes/solarized-light.yaml b/example/themes/solarized-light.yaml deleted file mode 100644 index 456833761f..0000000000 --- a/example/themes/solarized-light.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Solarized light - -themes: - solarized-light: - bg: [253, 246, 227] - red: [220, 50, 47] - green: [133, 153, 0] - yellow: [181, 137, 0] - blue: [38, 139, 210] - magenta: [211, 54, 130] - orange: [203, 75, 22] - fg: [101, 123, 131] - cyan: [42, 161, 152] - black: [7, 54, 66] - white: [238, 232, 213] diff --git a/example/themes/tokyo-night-dark.kdl b/example/themes/tokyo-night-dark.kdl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/example/themes/tokyo-night-light.kdl b/example/themes/tokyo-night-light.kdl new file mode 100644 index 0000000000..3ac5fb7674 --- /dev/null +++ b/example/themes/tokyo-night-light.kdl @@ -0,0 +1,16 @@ +themes { + tokyo-night-light { + fg 52 59 88 + bg 213 214 219 + black 15 15 20 + red 186 75 96 + green 72 94 48 + yellow 143 94 21 + blue 52 84 138 + magenta 90 74 120 + cyan 15 75 110 + white 130 137 172 + orange 150 80 39 + } +} + diff --git a/example/themes/tokyo-night-light.yaml b/example/themes/tokyo-night-light.yaml deleted file mode 100644 index 156943cd80..0000000000 --- a/example/themes/tokyo-night-light.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# TokyoNight Theme -# Originally by https://github.com/enkia/tokyo-night-vscode-theme - -themes: - tokyo-night-light: - fg: [52,59,88] #343B58 - bg: [213,214,219] #D5D6DB - black: [15,15,20] #0F0F14 - red: [186,75,96] #BA4B60 - green: [72,94,48] #485E30 - yellow: [143,94,21] #8F5E15 - blue: [52,84,138] #34548A - magenta: [90,74,120] #5A4A78 - cyan: [15,75,110] #0F4B6E - white: [130,137,172] #8289AC - orange: [150,80,39] #965027 diff --git a/example/themes/tokyo-night-storm.kdl b/example/themes/tokyo-night-storm.kdl new file mode 100644 index 0000000000..e727ef72f8 --- /dev/null +++ b/example/themes/tokyo-night-storm.kdl @@ -0,0 +1,16 @@ +themes { + tokyo-night-storm { + fg 169 177 214 + bg 36 40 59 + black 56 62 90 + red 249 51 87 + green 158 206 106 + yellow 224 175 104 + blue 122 162 247 + magenta 187 154 247 + cyan 42 195 222 + white 192 202 245 + orange 255 158 100 + } +} + diff --git a/example/themes/tokyo-night-storm.yaml b/example/themes/tokyo-night-storm.yaml deleted file mode 100644 index 9239990f90..0000000000 --- a/example/themes/tokyo-night-storm.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# TokyoNight Theme -# Originally by https://github.com/enkia/tokyo-night-vscode-theme - -themes: - tokyo-night-storm: - fg: [169,177,214] #A9B1D6 - bg: [36,40,59] #24283B - black: [56,62,90] #383E5A - red: [249,51,87] #F9334D - green: [158,206,106] #9ECE6A - yellow: [224,175,104] #E0AF68 - blue: [122,162,247] #7AA2F7 - magenta: [187,154,247] #BB9AF7 - cyan: [42,195,222] #2AC3DE - white: [192,202,245] #C0CAF5 - orange: [255,158,100] #FF9E64 diff --git a/example/themes/tokyo-night.kdl b/example/themes/tokyo-night.kdl new file mode 100644 index 0000000000..4ea1a8104a --- /dev/null +++ b/example/themes/tokyo-night.kdl @@ -0,0 +1,16 @@ +themes { + tokyo-night { + fg 169 177 214 + bg 26 27 38 + black 56 62 90 + red 249 51 87 + green 158 206 106 + yellow 224 175 104 + blue 122 162 247 + magenta 187 154 247 + cyan 42 195 222 + white 192 202 245 + orange 255 158 100 + } +} + diff --git a/example/themes/tokyo-night.yaml b/example/themes/tokyo-night.yaml deleted file mode 100644 index d62fb4228a..0000000000 --- a/example/themes/tokyo-night.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# TokyoNight Theme -# Originally by https://github.com/enkia/tokyo-night-vscode-theme - -themes: - tokyo-night: - fg: [169,177,214] #A9B1D6 - bg: [26,27,38] #1A1B26 - black: [56,62,90] #383E5A - red: [249,51,87] #F9334D - green: [158,206,106] #9ECE6A - yellow: [224,175,104] #E0AF68 - blue: [122,162,247] #7AA2F7 - magenta: [187,154,247] #BB9AF7 - cyan: [42,195,222] #2AC3DE - white: [192,202,245] #C0CAF5 - orange: [255,158,100] #FF9E64 diff --git a/src/commands.rs b/src/commands.rs index eeb1c8205e..f0b8ee9761 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,7 +11,7 @@ use std::path::PathBuf; use std::process; use zellij_utils::input::actions::Action; use zellij_client::start_client as start_client_impl; -use zellij_client::old_config_converter::config_yaml_to_config_kdl; +use zellij_client::old_config_converter::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl}; use zellij_client::{os_input_output::get_client_os_input, ClientInfo}; use zellij_server::os_input_output::get_server_os_input; use zellij_server::start_server as start_server_impl; @@ -163,13 +163,8 @@ pub(crate) fn convert_old_config_file(old_config_file: PathBuf, output_location: Ok(mut handle) => { let mut raw_config_file = String::new(); let _ = handle.read_to_string(&mut raw_config_file); - match config_yaml_to_config_kdl(&raw_config_file) { + match config_yaml_to_config_kdl(&raw_config_file, false) { Ok(kdl_config) => { - // TODO: CONTINUE HERE (18/09) - // - print/error in the main function (main.rs?) - DONE - // - add tests for Options + Keybinds - DONE - // - write conversions + tests for everything else - DONE - // - refactor and move on to converting layouts println!("{}", kdl_config); process::exit(0); }, @@ -184,7 +179,52 @@ pub(crate) fn convert_old_config_file(old_config_file: PathBuf, output_location: process::exit(1); } } +} +pub(crate) fn convert_old_layout_file(old_layout_file: PathBuf, output_location: Option) { + match File::open(&old_layout_file) { + Ok(mut handle) => { + let mut raw_layout_file = String::new(); + let _ = handle.read_to_string(&mut raw_layout_file); + match layout_yaml_to_layout_kdl(&raw_layout_file) { + Ok(kdl_layout) => { + println!("{}", kdl_layout); + process::exit(0); + }, + Err(e) => { + eprintln!("Failed to convert layout: {}", e); + process::exit(1); + } + } + }, + Err(e) => { + eprintln!("Failed to open file: {}", e); + process::exit(1); + } + } +} + +pub(crate) fn convert_old_theme_file(old_theme_file: PathBuf, output_location: Option) { + match File::open(&old_theme_file) { + Ok(mut handle) => { + let mut raw_config_file = String::new(); + let _ = handle.read_to_string(&mut raw_config_file); + match config_yaml_to_config_kdl(&raw_config_file, true) { + Ok(kdl_config) => { + println!("{}", kdl_config); + process::exit(0); + }, + Err(e) => { + eprintln!("Failed to convert config: {}", e); + process::exit(1); + } + } + }, + Err(e) => { + eprintln!("Failed to open file: {}", e); + process::exit(1); + } + } } fn attach_with_cli_client(cli_action: zellij_utils::cli::CliAction, session_name: &str) { diff --git a/src/main.rs b/src/main.rs index 43a610c36a..cc9a9f7dfc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,10 +30,18 @@ fn main() { commands::send_action_to_session(command_cli_action, opts.session); std::process::exit(0); } - if let Some(Command::Sessions(Sessions::Convert{ old_config_file, output })) = opts.command { + if let Some(Command::Sessions(Sessions::ConvertConfig { old_config_file, output })) = opts.command { commands::convert_old_config_file(old_config_file, output); std::process::exit(0); } + if let Some(Command::Sessions(Sessions::ConvertLayout { old_layout_file, output })) = opts.command { + commands::convert_old_layout_file(old_layout_file, output); + std::process::exit(0); + } + if let Some(Command::Sessions(Sessions::ConvertTheme { old_theme_file, output })) = opts.command { + commands::convert_old_theme_file(old_theme_file, output); + std::process::exit(0); + } } if let Some(Command::Sessions(Sessions::ListSessions)) = opts.command { diff --git a/zellij-client/src/old_config_converter/mod.rs b/zellij-client/src/old_config_converter/mod.rs index 4cb24b741c..1999a56f86 100644 --- a/zellij-client/src/old_config_converter/mod.rs +++ b/zellij-client/src/old_config_converter/mod.rs @@ -1,2 +1,4 @@ mod old_config; +mod old_layout; pub use old_config::config_yaml_to_config_kdl; +pub use old_layout::layout_yaml_to_layout_kdl; diff --git a/zellij-client/src/old_config_converter/old_config.rs b/zellij-client/src/old_config_converter/old_config.rs index 7661154241..ce8bd407af 100644 --- a/zellij-client/src/old_config_converter/old_config.rs +++ b/zellij-client/src/old_config_converter/old_config.rs @@ -4,7 +4,6 @@ // from it or changing it use std::fmt; use std::path::PathBuf; -// use thiserror::Error; use url::Url; use serde::{Deserialize, Deserializer, Serialize}; @@ -132,28 +131,36 @@ const THEME_DIR_DESCRIPTION: &'static str = " // "; -fn options_yaml_to_options_kdl(options_yaml: &OldOptions) -> String { +fn options_yaml_to_options_kdl(options_yaml: &OldOptions, no_comments: bool) -> String { let mut options_kdl = String::new(); macro_rules! push_option { ($attribute_name:ident, $description_text:ident, $present_pattern:expr) => { - options_kdl.push_str($description_text); + if !no_comments { + options_kdl.push_str($description_text); + } if let Some($attribute_name) = &options_yaml.$attribute_name { options_kdl.push_str(&format!($present_pattern, $attribute_name)); options_kdl.push('\n'); }; }; ($attribute_name:ident, $description_text:ident, $present_pattern:expr, $absent_pattern:expr) => { - options_kdl.push_str($description_text); + if !no_comments { + options_kdl.push_str($description_text); + } match &options_yaml.$attribute_name { Some($attribute_name) => { options_kdl.push_str(&format!($present_pattern, $attribute_name)); }, None => { - options_kdl.push_str(&format!($absent_pattern)); + if !no_comments { + options_kdl.push_str(&format!($absent_pattern)); + } } }; - options_kdl.push('\n'); + if !no_comments || options_yaml.$attribute_name.is_some() { + options_kdl.push('\n'); + } } } @@ -350,7 +357,7 @@ fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String kdl_keybinds } -pub fn config_yaml_to_config_kdl(raw_yaml_config: &str) -> Result { // returns the raw kdl config +pub fn config_yaml_to_config_kdl(raw_yaml_config: &str, no_comments: bool) -> Result { // returns the raw kdl config let config_from_yaml: OldConfigFromYaml = serde_yaml::from_str(raw_yaml_config).map_err(|e| format!("Failed to parse yaml: {:?}", e))?; let mut kdl_config = String::new(); if let Some(old_config_keybinds) = config_from_yaml.keybinds.as_ref() { @@ -360,7 +367,7 @@ pub fn config_yaml_to_config_kdl(raw_yaml_config: &str) -> Result Result, pub keybinds: Option, @@ -398,7 +405,7 @@ struct OldConfigFromYaml { } #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -struct OldKeybindsFromYaml { +pub struct OldKeybindsFromYaml { #[serde(flatten)] keybinds: HashMap>, #[serde(default)] @@ -459,7 +466,7 @@ struct OldKeybinds(HashMap); struct OldModeKeybinds(BTreeMap>); #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -struct OldThemesFromYamlIntermediate(HashMap); +pub struct OldThemesFromYamlIntermediate(HashMap); #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] struct OldPaletteFromYaml { @@ -555,22 +562,22 @@ struct OldTheme { } #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] -struct OldUiConfigFromYaml { +pub struct OldUiConfigFromYaml { pub pane_frames: OldFrameConfigFromYaml, } #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] -struct OldFrameConfigFromYaml { +pub struct OldFrameConfigFromYaml { pub rounded_corners: bool, } #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] -struct OldEnvironmentVariablesFromYaml { +pub struct OldEnvironmentVariablesFromYaml { env: HashMap, } #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] -struct OldPluginsConfigFromYaml(Vec); +pub struct OldPluginsConfigFromYaml(Vec); #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] struct OldPluginConfigFromYaml { @@ -632,7 +639,7 @@ enum OldPluginType { } #[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] -enum OldOnForceClose { +pub enum OldOnForceClose { #[serde(alias = "quit")] Quit, #[serde(alias = "detach")] @@ -655,7 +662,7 @@ impl Default for OldOnForceClose { } #[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -enum OldClipboard { +pub enum OldClipboard { #[serde(alias = "system")] System, #[serde(alias = "primary")] @@ -678,7 +685,7 @@ impl Default for OldClipboard { } #[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize)] -struct OldOptions { +pub struct OldOptions { #[serde(default)] pub simplified_ui: Option, pub theme: Option, @@ -707,7 +714,7 @@ struct OldOptions { /// Describes the different input modes, which change the way that keystrokes will be interpreted. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] -enum OldInputMode { +pub enum OldInputMode { /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading /// to other modes #[serde(alias = "normal")] @@ -1131,7 +1138,7 @@ struct OldRunPluginFromYaml { } #[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)] -struct OldRunCommand { +pub struct OldRunCommand { #[serde(alias = "cmd")] pub command: PathBuf, #[serde(default)] diff --git a/zellij-client/src/old_config_converter/old_layout.rs b/zellij-client/src/old_config_converter/old_layout.rs index 3f0b392957..fabe95ab9a 100644 --- a/zellij-client/src/old_config_converter/old_layout.rs +++ b/zellij-client/src/old_config_converter/old_layout.rs @@ -1,417 +1,282 @@ -//! The layout system. -// Layouts have been moved from [`zellij-server`] to -// [`zellij-utils`] in order to provide more helpful -// error messages to the user until a more general -// logging system is in place. -// In case there is a logging system in place evaluate, -// if [`zellij-utils`], or [`zellij-server`] is a proper -// place. -// If plugins should be able to depend on the layout system -// then [`zellij-utils`] could be a proper place. -use crate::{ - input::{ - command::RunCommand, - config::{ConfigError, LayoutNameInTabError}, - }, - pane_size::{Dimension, PaneGeom}, - setup, -}; - +// This is a converter from the old yaml layout to the new KDL layout. +// +// It is supposed to be mostly self containing - please refrain from adding to it, importing +// from it or changing it use super::{ - config::ConfigFromYaml, - plugins::{PluginTag, PluginsConfigError}, + old_config::{OldConfigFromYaml, OldRunCommand, config_yaml_to_config_kdl}, }; use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; use std::vec::Vec; use std::{ - cmp::max, - fmt, fs, - ops::Not, - path::{Path, PathBuf}, + fmt, + path::PathBuf, }; -use std::{fs::File, io::prelude::*}; use url::Url; +fn pane_line(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool) -> String { + let mut pane_line = format!("pane"); + if let Some(pane_name) = pane_name { + pane_line.push_str(&format!(" name=\"{}\"", pane_name)); + } + if let Some(split_size) = split_size { + pane_line.push_str(&format!(" size={}", split_size)); + } + if let Some(focus) = focus { + pane_line.push_str(&format!(" focus={}", focus)); + } + if borderless { + pane_line.push_str(" borderless=true"); + } + pane_line +} + +fn tab_line(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool) -> String { + let mut pane_line = format!("tab"); + if let Some(pane_name) = pane_name { + pane_line.push_str(&format!(" name=\"{}\"", pane_name)); + } + if let Some(split_size) = split_size { + pane_line.push_str(&format!(" size={}", split_size)); + } + if let Some(focus) = focus { + pane_line.push_str(&format!(" focus={}", focus)); + } + if borderless { + pane_line.push_str(" borderless=true"); + } + pane_line +} + +fn pane_line_with_children(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool, split_direction: OldDirection) -> String { + let mut pane_line = format!("pane"); + if let Some(pane_name) = pane_name { + pane_line.push_str(&format!(" name=\"{}\"", pane_name)); + } + if let Some(split_size) = split_size { + pane_line.push_str(&format!(" size={}", split_size)); + } + if let Some(focus) = focus { + pane_line.push_str(&format!(" focus={}", focus)); + } + pane_line.push_str(&format!(" split_direction=\"{}\"", split_direction)); + if borderless { + pane_line.push_str(" borderless=true"); + } + pane_line +} + +fn pane_command_line(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool, command: &PathBuf) -> String { + let mut pane_line = format!("pane command={:?}", command); + if let Some(pane_name) = pane_name { + pane_line.push_str(&format!(" name=\"{}\"", pane_name)); + } + if let Some(split_size) = split_size { + pane_line.push_str(&format!(" size={}", split_size)); + } + if let Some(focus) = focus { + pane_line.push_str(&format!(" focus={}", focus)); + } + if borderless { + pane_line.push_str(" borderless=true"); + } + pane_line +} + +fn tab_line_with_children(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool, split_direction: OldDirection) -> String { + let mut pane_line = format!("tab"); + if let Some(pane_name) = pane_name { + pane_line.push_str(&format!(" name=\"{}\"", pane_name)); + } + if let Some(split_size) = split_size { + pane_line.push_str(&format!(" size={}", split_size)); + } + if let Some(focus) = focus { + pane_line.push_str(&format!(" focus={}", focus)); + } + pane_line.push_str(&format!(" split_direction=\"{}\"", split_direction)); + if borderless { + pane_line.push_str(" borderless=true"); + } + pane_line +} + +fn stringify_template(template: &OldLayoutTemplate, indentation: String, has_no_tabs: bool, is_base: bool) -> String { + let mut stringified = if is_base { String::new() } else { String::from("\n") }; + if is_base && !template.parts.is_empty() && template.direction == OldDirection::Vertical { + // we don't support specifying the split direction in the layout node + // eg. layout split_direction="Vertical" { .. } <== this is not supported!! + // so we need to add a child wrapper with the split direction instead: + // layout { + // pane split_direction="Vertical" { .. } + // } + let child_indentation = format!("{} ", &indentation); + stringified.push_str(&stringify_template(template, child_indentation, has_no_tabs, false)); + } else if !template.parts.is_empty() { + if !is_base { + stringified.push_str(&format!("{}{} {{", indentation, pane_line_with_children(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless, template.direction))); + } + for part in &template.parts { + let child_indentation = format!("{} ", &indentation); + stringified.push_str(&stringify_template(&part, child_indentation, has_no_tabs, false)); + } + if !is_base { + stringified.push_str(&format!("\n{}}}", indentation)); + } + } else if template.body && !has_no_tabs { + stringified.push_str(&format!("{}children", indentation)); + } else { + match template.run.as_ref() { + Some(OldRunFromYaml::Plugin(plugin_from_yaml)) => { + stringified.push_str(&format!("{}{} {{\n", &indentation, pane_line(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless))); + stringified.push_str(&format!("{} plugin location=\"{}\"\n", &indentation, plugin_from_yaml.location)); + stringified.push_str(&format!("{}}}", &indentation)); + }, + Some(OldRunFromYaml::Command(command_from_yaml)) => { + stringified.push_str(&format!("{}{}", &indentation, &pane_command_line(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless, &command_from_yaml.command))); + if let Some(cwd) = command_from_yaml.cwd.as_ref() { + stringified.push_str(&format!(" cwd={:?}", cwd)); + } + if !command_from_yaml.args.is_empty() { + stringified.push_str(" {\n"); + stringified.push_str(&format!("{} args {}\n", &indentation, command_from_yaml.args.iter().map(|s| format!("\"{}\"", s)).collect::>().join(" "))); + stringified.push_str(&format!("{}}}", &indentation)); + } + }, + None => { + stringified.push_str(&format!("{}{}", &indentation, pane_line(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless))); + } + }; + } + stringified +} + +fn stringify_tabs(tabs: Vec) -> String { + let mut stringified = String::new(); + for tab in tabs { + let child_indentation = String::from(" "); + if !tab.parts.is_empty() { + stringified.push_str(&format!("\n{}{} {{", child_indentation, tab_line_with_children(tab.pane_name.as_ref(), tab.split_size, tab.focus, tab.borderless, tab.direction))); + let tab_template = OldLayoutTemplate::from(tab); + stringified.push_str(&stringify_template(&tab_template, child_indentation.clone(), true, true)); + stringified.push_str(&format!("\n{}}}", child_indentation)); + } else { + stringified.push_str(&format!("\n{}{}", child_indentation, tab_line(tab.pane_name.as_ref(), tab.split_size, tab.focus, tab.borderless))); + } + } + stringified +} + +pub fn layout_yaml_to_layout_kdl(raw_yaml_layout: &str) -> Result { // returns the raw kdl config + let layout_from_yaml: OldLayoutFromYamlIntermediate = serde_yaml::from_str(raw_yaml_layout).map_err(|e| format!("Failed to parse yaml: {:?}", e))?; + let mut kdl_layout = String::new(); + kdl_layout.push_str("layout {"); + let template = layout_from_yaml.template; + let tabs = layout_from_yaml.tabs; + let has_no_tabs = tabs.is_empty() || tabs.len() == 1 && tabs.get(0).map(|t| t.parts.is_empty()).unwrap_or(false); + if has_no_tabs { + let indentation = String::from(""); + kdl_layout.push_str(&stringify_template(&template, indentation, has_no_tabs, true)); + } else { + kdl_layout.push_str("\n default_tab_template {"); + let indentation = String::from(" "); + kdl_layout.push_str(&stringify_template(&template, indentation, has_no_tabs, true)); + kdl_layout.push_str("\n }"); + kdl_layout.push_str(&stringify_tabs(tabs)); + } + kdl_layout.push_str("\n}"); + let layout_config = config_yaml_to_config_kdl(raw_yaml_layout, true)?; + if let Some(session_name) = layout_from_yaml.session.name { + kdl_layout.push_str(&format!("\nsession_name \"{}\"", session_name)); + if let Some(attach_to_session) = layout_from_yaml.session.attach { + kdl_layout.push_str(&format!("\nattach_to_session {}", attach_to_session)); + } + } + if !layout_config.is_empty() { + kdl_layout.push('\n'); + } + kdl_layout.push_str(&layout_config); + Ok(kdl_layout) +} + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] -pub enum Direction { +pub enum OldDirection { #[serde(alias = "horizontal")] Horizontal, #[serde(alias = "vertical")] Vertical, } -impl Not for Direction { - type Output = Self; - - fn not(self) -> Self::Output { +impl fmt::Display for OldDirection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { - Direction::Horizontal => Direction::Vertical, - Direction::Vertical => Direction::Horizontal, + Self::Horizontal => write!(f, "Horizontal"), + Self::Vertical => write!(f, "Vertical"), } } } #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] -pub enum SplitSize { +pub enum OldSplitSize { #[serde(alias = "percent")] Percent(u64), // 1 to 100 #[serde(alias = "fixed")] Fixed(usize), // An absolute number of columns or rows } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub enum Run { - #[serde(rename = "plugin")] - Plugin(RunPlugin), - #[serde(rename = "command")] - Command(RunCommand), +impl fmt::Display for OldSplitSize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + Self::Percent(percent) => write!(f, "\"{}%\"", percent), + Self::Fixed(fixed_size) => write!(f, "{}", fixed_size), + } + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub enum RunFromYaml { +pub enum OldRunFromYaml { #[serde(rename = "plugin")] - Plugin(RunPluginFromYaml), + Plugin(OldRunPluginFromYaml), #[serde(rename = "command")] - Command(RunCommand), + Command(OldRunCommand), } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct RunPluginFromYaml { +pub struct OldRunPluginFromYaml { #[serde(default)] pub _allow_exec_host_cmd: bool, pub location: Url, } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct RunPlugin { - #[serde(default)] - pub _allow_exec_host_cmd: bool, - pub location: RunPluginLocation, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub enum RunPluginLocation { - File(PathBuf), - Zellij(PluginTag), -} - -impl From<&RunPluginLocation> for Url { - fn from(location: &RunPluginLocation) -> Self { - let url = match location { - RunPluginLocation::File(path) => format!( - "file:{}", - path.clone().into_os_string().into_string().unwrap() - ), - RunPluginLocation::Zellij(tag) => format!("zellij:{}", tag), - }; - Self::parse(&url).unwrap() - } -} - -impl fmt::Display for RunPluginLocation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - Self::File(path) => write!( - f, - "{}", - path.clone().into_os_string().into_string().unwrap() - ), - - Self::Zellij(tag) => write!(f, "{}", tag), - } - } -} - -// The layout struct ultimately used to build the layouts. -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub struct Layout { - pub direction: Direction, - #[serde(default)] - pub pane_name: Option, - #[serde(default)] - pub parts: Vec, - pub split_size: Option, - pub run: Option, - #[serde(default)] - pub borderless: bool, - pub focus: Option, -} - -// The struct that is used to deserialize the layout from -// a yaml configuration file, is needed because of: -// https://github.com/bincode-org/bincode/issues/245 -// flattened fields don't retain size information. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(default)] -pub struct LayoutFromYamlIntermediate { +pub struct OldLayoutFromYamlIntermediate { #[serde(default)] - pub template: LayoutTemplate, + pub template: OldLayoutTemplate, #[serde(default)] pub borderless: bool, #[serde(default)] - pub tabs: Vec, + pub tabs: Vec, #[serde(default)] - pub session: SessionFromYaml, + pub session: OldSessionFromYaml, #[serde(flatten)] - pub config: Option, + pub config: Option, } -// The struct that is used to deserialize the layout from -// a yaml configuration file #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] #[serde(default)] -pub struct LayoutFromYaml { +pub struct OldLayoutFromYaml { #[serde(default)] - pub session: SessionFromYaml, + pub session: OldSessionFromYaml, #[serde(default)] - pub template: LayoutTemplate, + pub template: OldLayoutTemplate, #[serde(default)] pub borderless: bool, #[serde(default)] - pub tabs: Vec, + pub tabs: Vec, } -type LayoutFromYamlIntermediateResult = Result; - -impl LayoutFromYamlIntermediate { - pub fn from_path(layout_path: &Path) -> LayoutFromYamlIntermediateResult { - let mut layout_file = File::open(&layout_path) - .or_else(|_| File::open(&layout_path.with_extension("yaml"))) - .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; - - let mut layout = String::new(); - layout_file.read_to_string(&mut layout)?; - let layout: Option = match serde_yaml::from_str(&layout) { - Err(e) => { - // needs direct check, as `[ErrorImpl]` is private - // https://github.com/dtolnay/serde-yaml/issues/121 - if layout.is_empty() { - return Ok(LayoutFromYamlIntermediate::default()); - } - return Err(ConfigError::Serde(e)); - }, - Ok(config) => config, - }; - - match layout { - Some(layout) => { - for tab in layout.tabs.clone() { - tab.check()?; - } - Ok(layout) - }, - None => Ok(LayoutFromYamlIntermediate::default()), - } - } - - pub fn from_yaml(yaml: &str) -> LayoutFromYamlIntermediateResult { - let layout: LayoutFromYamlIntermediate = match serde_yaml::from_str(yaml) { - Err(e) => { - // needs direct check, as `[ErrorImpl]` is private - // https://github.com/dtolnay/serde-yaml/issues/121 - if yaml.is_empty() { - return Ok(LayoutFromYamlIntermediate::default()); - } - return Err(ConfigError::Serde(e)); - }, - Ok(config) => config, - }; - Ok(layout) - } - - pub fn to_layout_and_config(&self) -> (LayoutFromYaml, Option) { - let config = self.config.clone(); - let layout = self.clone().into(); - (layout, config) - } - - pub fn from_path_or_default( - layout: Option<&PathBuf>, - layout_dir: Option, - ) -> Option { - layout - .map(|layout| { - // The way we determine where to look for the layout is similar to - // how a path would look for an executable. - // See the gh issue for more: https://github.com/zellij-org/zellij/issues/1412#issuecomment-1131559720 - if layout.extension().is_some() || layout.components().count() > 1 { - // We look localy! - LayoutFromYamlIntermediate::from_path(layout) - } else { - // We look in the default dir - LayoutFromYamlIntermediate::from_dir(layout, layout_dir.as_ref()) - } - }) - .or_else(|| { - Some(LayoutFromYamlIntermediate::from_dir( - &std::path::PathBuf::from("default"), - layout_dir.as_ref(), - )) - }) - } - - // It wants to use Path here, but that doesn't compile. - #[allow(clippy::ptr_arg)] - pub fn from_dir( - layout: &PathBuf, - layout_dir: Option<&PathBuf>, - ) -> LayoutFromYamlIntermediateResult { - match layout_dir { - Some(dir) => { - let layout_path = &dir.join(layout); - if layout_path.with_extension("yaml").exists() { - Self::from_path(layout_path) - } else { - LayoutFromYamlIntermediate::from_default_assets(layout) - } - }, - None => LayoutFromYamlIntermediate::from_default_assets(layout), - } - } - // Currently still needed but on nightly - // this is already possible: - // HashMap<&'static str, Vec> - pub fn from_default_assets(path: &Path) -> LayoutFromYamlIntermediateResult { - match path.to_str() { - Some("default") => Self::default_from_assets(), - Some("strider") => Self::strider_from_assets(), - Some("disable-status-bar") => Self::disable_status_from_assets(), - Some("compact") => Self::compact_from_assets(), - None | Some(_) => Err(ConfigError::IoPath( - std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), - path.into(), - )), - } - } - - // TODO Deserialize the assets from bytes &[u8], - // once serde-yaml supports zero-copy - pub fn default_from_assets() -> LayoutFromYamlIntermediateResult { - let layout: LayoutFromYamlIntermediate = - serde_yaml::from_str(&String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)?; - Ok(layout) - } - - pub fn strider_from_assets() -> LayoutFromYamlIntermediateResult { - let layout: LayoutFromYamlIntermediate = - serde_yaml::from_str(&String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)?; - Ok(layout) - } - - pub fn disable_status_from_assets() -> LayoutFromYamlIntermediateResult { - let layout: LayoutFromYamlIntermediate = - serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?; - Ok(layout) - } - - pub fn compact_from_assets() -> LayoutFromYamlIntermediateResult { - let layout: LayoutFromYamlIntermediate = - serde_yaml::from_str(&String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)?; - Ok(layout) - } -} - -type LayoutFromYamlResult = Result; - -impl LayoutFromYaml { - pub fn new(layout_path: &Path) -> LayoutFromYamlResult { - let mut layout_file = File::open(&layout_path) - .or_else(|_| File::open(&layout_path.with_extension("yaml"))) - .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; - - let mut layout = String::new(); - layout_file.read_to_string(&mut layout)?; - let layout: Option = match serde_yaml::from_str(&layout) { - Err(e) => { - // needs direct check, as `[ErrorImpl]` is private - // https://github.com/dtolnay/serde-yaml/issues/121 - if layout.is_empty() { - return Ok(LayoutFromYaml::default()); - } - return Err(ConfigError::Serde(e)); - }, - Ok(config) => config, - }; - - match layout { - Some(layout) => { - for tab in layout.tabs.clone() { - tab.check()?; - } - Ok(layout) - }, - None => Ok(LayoutFromYaml::default()), - } - } - - // It wants to use Path here, but that doesn't compile. - #[allow(clippy::ptr_arg)] - pub fn from_dir(layout: &PathBuf, layout_dir: Option<&PathBuf>) -> LayoutFromYamlResult { - match layout_dir { - Some(dir) => { - Self::new(&dir.join(layout)).or_else(|_| Self::from_default_assets(layout)) - }, - None => Self::from_default_assets(layout), - } - } - - pub fn from_path_or_default( - layout: Option<&PathBuf>, - layout_path: Option<&PathBuf>, - layout_dir: Option, - ) -> Option { - layout - .map(|p| LayoutFromYaml::from_dir(p, layout_dir.as_ref())) - .or_else(|| layout_path.map(|p| LayoutFromYaml::new(p))) - .or_else(|| { - Some(LayoutFromYaml::from_dir( - &std::path::PathBuf::from("default"), - layout_dir.as_ref(), - )) - }) - } - - // Currently still needed but on nightly - // this is already possible: - // HashMap<&'static str, Vec> - pub fn from_default_assets(path: &Path) -> LayoutFromYamlResult { - match path.to_str() { - Some("default") => Self::default_from_assets(), - Some("strider") => Self::strider_from_assets(), - Some("disable-status-bar") => Self::disable_status_from_assets(), - None | Some(_) => Err(ConfigError::IoPath( - std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), - path.into(), - )), - } - } - - // TODO Deserialize the assets from bytes &[u8], - // once serde-yaml supports zero-copy - pub fn default_from_assets() -> LayoutFromYamlResult { - let layout: LayoutFromYaml = - serde_yaml::from_str(&String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)?; - Ok(layout) - } - - pub fn strider_from_assets() -> LayoutFromYamlResult { - let layout: LayoutFromYaml = - serde_yaml::from_str(&String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)?; - Ok(layout) - } - - pub fn disable_status_from_assets() -> LayoutFromYamlResult { - let layout: LayoutFromYaml = - serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?; - Ok(layout) - } -} - -// The struct that is used to deserialize the session from -// a yaml configuration file #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] -pub struct SessionFromYaml { +pub struct OldSessionFromYaml { pub name: Option, #[serde(default = "default_as_some_true")] pub attach: Option, @@ -421,275 +286,85 @@ fn default_as_some_true() -> Option { Some(true) } -// The struct that carries the information template that is used to -// construct the layout #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub struct LayoutTemplate { - pub direction: Direction, +pub struct OldLayoutTemplate { + pub direction: OldDirection, #[serde(default)] pub pane_name: Option, #[serde(default)] pub borderless: bool, #[serde(default)] - pub parts: Vec, + pub parts: Vec, #[serde(default)] pub body: bool, - pub split_size: Option, + pub split_size: Option, pub focus: Option, - pub run: Option, + pub run: Option, } -impl LayoutTemplate { - // Insert an optional `[TabLayout]` at the correct position - pub fn insert_tab_layout(mut self, tab_layout: Option) -> Self { - if self.body { - return tab_layout.unwrap_or_default().into(); - } - for (i, part) in self.parts.clone().iter().enumerate() { - if part.body { - self.parts.push(tab_layout.unwrap_or_default().into()); - self.parts.swap_remove(i); - break; - } - // recurse - let new_part = part.clone().insert_tab_layout(tab_layout.clone()); - self.parts.push(new_part); - self.parts.swap_remove(i); - } - self - } - - fn from_vec_tab_layout(tab_layout: Vec) -> Vec { - tab_layout - .iter() - .map(|tab_layout| Self::from(tab_layout.to_owned())) - .collect() - } -} - -// The tab-layout struct used to specify each individual tab. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct TabLayout { +pub struct OldTabLayout { #[serde(default)] - pub direction: Direction, + pub direction: OldDirection, pub pane_name: Option, #[serde(default)] pub borderless: bool, #[serde(default)] - pub parts: Vec, - pub split_size: Option, + pub parts: Vec, + pub split_size: Option, #[serde(default)] pub name: String, pub focus: Option, - pub run: Option, -} - -impl TabLayout { - fn check(&self) -> Result { - for part in &self.parts { - part.check()?; - if !part.name.is_empty() { - return Err(ConfigError::LayoutNameInTab(LayoutNameInTabError)); - } - } - Ok(self.clone()) - } -} - -impl Layout { - pub fn total_terminal_panes(&self) -> usize { - let mut total_panes = 0; - total_panes += self.parts.len(); - for part in &self.parts { - match part.run { - Some(Run::Command(_)) | None => { - total_panes += part.total_terminal_panes(); - }, - Some(Run::Plugin(_)) => {}, - } - } - total_panes - } - - pub fn total_borderless_panes(&self) -> usize { - let mut total_borderless_panes = 0; - total_borderless_panes += self.parts.iter().filter(|p| p.borderless).count(); - for part in &self.parts { - total_borderless_panes += part.total_borderless_panes(); - } - total_borderless_panes - } - pub fn extract_run_instructions(&self) -> Vec> { - let mut run_instructions = vec![]; - if self.parts.is_empty() { - run_instructions.push(self.run.clone()); - } - for part in &self.parts { - let mut current_runnables = part.extract_run_instructions(); - run_instructions.append(&mut current_runnables); - } - run_instructions - } - - pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(Layout, PaneGeom)> { - split_space(space, self) - } - - pub fn merge_layout_parts(&mut self, mut parts: Vec) { - self.parts.append(&mut parts); - } - - fn from_vec_tab_layout(tab_layout: Vec) -> Result, ConfigError> { - tab_layout - .iter() - .map(|tab_layout| Layout::try_from(tab_layout.to_owned())) - .collect() - } - - fn from_vec_template_layout( - layout_template: Vec, - ) -> Result, ConfigError> { - layout_template - .iter() - .map(|layout_template| Layout::try_from(layout_template.to_owned())) - .collect() - } -} - -fn layout_size(direction: Direction, layout: &Layout) -> usize { - fn child_layout_size( - direction: Direction, - parent_direction: Direction, - layout: &Layout, - ) -> usize { - let size = if parent_direction == direction { 1 } else { 0 }; - if layout.parts.is_empty() { - size - } else { - let children_size = layout - .parts - .iter() - .map(|p| child_layout_size(direction, layout.direction, p)) - .sum(); - max(size, children_size) - } - } - child_layout_size(direction, direction, layout) -} - -fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneGeom)> { - let mut pane_positions = Vec::new(); - let sizes: Vec> = layout.parts.iter().map(|part| part.split_size).collect(); - - let mut split_geom = Vec::new(); - let (mut current_position, split_dimension_space, mut inherited_dimension) = - match layout.direction { - Direction::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows), - Direction::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols), - }; - - let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); - - for (&size, part) in sizes.iter().zip(&layout.parts) { - let split_dimension = match size { - Some(SplitSize::Percent(percent)) => Dimension::percent(percent as f64), - Some(SplitSize::Fixed(size)) => Dimension::fixed(size), - None => { - let free_percent = if let Some(p) = split_dimension_space.as_percent() { - p - sizes - .iter() - .map(|&s| { - if let Some(SplitSize::Percent(ip)) = s { - ip as f64 - } else { - 0.0 - } - }) - .sum::() - } else { - panic!("Implicit sizing within fixed-size panes is not supported"); - }; - Dimension::percent(free_percent / flex_parts as f64) - }, - }; - inherited_dimension.set_inner( - layout - .parts - .iter() - .map(|p| layout_size(!layout.direction, p)) - .max() - .unwrap(), - ); - let geom = match layout.direction { - Direction::Vertical => PaneGeom { - x: current_position, - y: space_to_split.y, - cols: split_dimension, - rows: inherited_dimension, - }, - Direction::Horizontal => PaneGeom { - x: space_to_split.x, - y: current_position, - cols: inherited_dimension, - rows: split_dimension, - }, - }; - split_geom.push(geom); - current_position += layout_size(layout.direction, part); - } - - for (i, part) in layout.parts.iter().enumerate() { - let part_position_and_size = split_geom.get(i).unwrap(); - if !part.parts.is_empty() { - let mut part_positions = split_space(part_position_and_size, part); - pane_positions.append(&mut part_positions); - } else { - pane_positions.push((part.clone(), *part_position_and_size)); + pub run: Option, +} + +impl From for OldLayoutTemplate { + fn from(old_tab_layout: OldTabLayout) -> Self { + OldLayoutTemplate { + direction: old_tab_layout.direction, + pane_name: old_tab_layout.pane_name.clone(), + borderless: old_tab_layout.borderless, + parts: old_tab_layout.parts.iter().map(|o| o.into()).collect(), + split_size: old_tab_layout.split_size, + focus: old_tab_layout.focus, + run: old_tab_layout.run.clone(), + body: false, } } - pane_positions } -impl TryFrom for RunPluginLocation { - type Error = PluginsConfigError; - - fn try_from(url: Url) -> Result { - match url.scheme() { - "zellij" => Ok(Self::Zellij(PluginTag::new(url.path()))), - "file" => { - let path = PathBuf::from(url.path()); - let canonicalize = |p: &Path| { - fs::canonicalize(p) - .map_err(|_| PluginsConfigError::InvalidPluginLocation(p.to_owned())) - }; - canonicalize(&path) - .or_else(|_| match path.strip_prefix("/") { - Ok(path) => canonicalize(path), - Err(_) => Err(PluginsConfigError::InvalidPluginLocation(path.to_owned())), - }) - .map(Self::File) - }, - _ => Err(PluginsConfigError::InvalidUrl(url)), +impl From<&OldTabLayout> for OldLayoutTemplate { + fn from(old_tab_layout: &OldTabLayout) -> Self { + OldLayoutTemplate { + direction: old_tab_layout.direction, + pane_name: old_tab_layout.pane_name.clone(), + borderless: old_tab_layout.borderless, + parts: old_tab_layout.parts.iter().map(|o| o.into()).collect(), + split_size: old_tab_layout.split_size, + focus: old_tab_layout.focus, + run: old_tab_layout.run.clone(), + body: false, } } } -impl TryFrom for Run { - type Error = PluginsConfigError; - - fn try_from(run: RunFromYaml) -> Result { - match run { - RunFromYaml::Command(command) => Ok(Run::Command(command)), - RunFromYaml::Plugin(plugin) => Ok(Run::Plugin(RunPlugin { - _allow_exec_host_cmd: plugin._allow_exec_host_cmd, - location: plugin.location.try_into()?, - })), +impl From<&mut OldTabLayout> for OldLayoutTemplate { + fn from(old_tab_layout: &mut OldTabLayout) -> Self { + OldLayoutTemplate { + direction: old_tab_layout.direction, + pane_name: old_tab_layout.pane_name.clone(), + borderless: old_tab_layout.borderless, + parts: old_tab_layout.parts.iter().map(|o| o.into()).collect(), + split_size: old_tab_layout.split_size, + focus: old_tab_layout.focus, + run: old_tab_layout.run.clone(), + body: false, } } } -impl From for LayoutFromYaml { - fn from(layout_from_yaml_intermediate: LayoutFromYamlIntermediate) -> Self { +impl From for OldLayoutFromYaml { + fn from(layout_from_yaml_intermediate: OldLayoutFromYamlIntermediate) -> Self { Self { template: layout_from_yaml_intermediate.template, borderless: layout_from_yaml_intermediate.borderless, @@ -699,8 +374,8 @@ impl From for LayoutFromYaml { } } -impl From for LayoutFromYamlIntermediate { - fn from(layout_from_yaml: LayoutFromYaml) -> Self { +impl From for OldLayoutFromYamlIntermediate { + fn from(layout_from_yaml: OldLayoutFromYaml) -> Self { Self { template: layout_from_yaml.template, borderless: layout_from_yaml.borderless, @@ -711,88 +386,21 @@ impl From for LayoutFromYamlIntermediate { } } -impl Default for LayoutFromYamlIntermediate { +impl Default for OldLayoutFromYamlIntermediate { fn default() -> Self { - LayoutFromYaml::default().into() - } -} - -impl TryFrom for Layout { - type Error = ConfigError; - - fn try_from(tab: TabLayout) -> Result { - Ok(Layout { - direction: tab.direction, - pane_name: tab.pane_name, - borderless: tab.borderless, - parts: Self::from_vec_tab_layout(tab.parts)?, - split_size: tab.split_size, - focus: tab.focus, - run: tab.run.map(Run::try_from).transpose()?, - }) - } -} - -impl From for LayoutTemplate { - fn from(tab: TabLayout) -> Self { - Self { - direction: tab.direction, - pane_name: tab.pane_name, - borderless: tab.borderless, - parts: Self::from_vec_tab_layout(tab.parts), - body: false, - split_size: tab.split_size, - focus: tab.focus, - run: tab.run, - } - } -} - -impl TryFrom for Layout { - type Error = ConfigError; - - fn try_from(template: LayoutTemplate) -> Result { - Ok(Layout { - direction: template.direction, - pane_name: template.pane_name, - borderless: template.borderless, - parts: Self::from_vec_template_layout(template.parts)?, - split_size: template.split_size, - focus: template.focus, - run: template - .run - .map(Run::try_from) - // FIXME: This is just Result::transpose but that method is unstable, when it - // stabalizes we should swap this out. - .map_or(Ok(None), |r| r.map(Some))?, - }) - } -} - -impl Default for TabLayout { - fn default() -> Self { - Self { - direction: Direction::Horizontal, - borderless: false, - parts: vec![], - split_size: None, - run: None, - name: String::new(), - pane_name: None, - focus: None, - } + OldLayoutFromYaml::default().into() } } -impl Default for LayoutTemplate { +impl Default for OldLayoutTemplate { fn default() -> Self { Self { - direction: Direction::Horizontal, + direction: OldDirection::Horizontal, pane_name: None, body: false, borderless: false, - parts: vec![LayoutTemplate { - direction: Direction::Horizontal, + parts: vec![OldLayoutTemplate { + direction: OldDirection::Horizontal, pane_name: None, body: true, borderless: false, @@ -808,13 +416,13 @@ impl Default for LayoutTemplate { } } -impl Default for Direction { +impl Default for OldDirection { fn default() -> Self { - Direction::Horizontal + OldDirection::Horizontal } } // The unit test location. +#[path = "./unit/convert_layout_tests.rs"] #[cfg(test)] -#[path = "./unit/layout_test.rs"] -mod layout_test; +mod convert_layout_test; diff --git a/zellij-client/src/old_config_converter/unit/convert_config_tests.rs b/zellij-client/src/old_config_converter/unit/convert_config_tests.rs index 0b93cf7a89..f46f638267 100644 --- a/zellij-client/src/old_config_converter/unit/convert_config_tests.rs +++ b/zellij-client/src/old_config_converter/unit/convert_config_tests.rs @@ -9,7 +9,7 @@ fn properly_convert_default_config() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -20,7 +20,7 @@ fn convert_config_with_custom_options() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -31,7 +31,7 @@ fn convert_config_with_keybind_unbinds_in_mode() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -42,7 +42,7 @@ fn convert_config_with_global_keybind_unbinds() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -53,7 +53,7 @@ fn convert_config_with_unbind_all_keys_per_mode() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -64,7 +64,7 @@ fn convert_config_with_env_variables() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -75,7 +75,7 @@ fn convert_config_with_ui_config() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } @@ -86,7 +86,7 @@ fn convert_config_with_themes_config() -> Result<(), String> { let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; - let kdl_config = config_yaml_to_config_kdl(&raw_config_file)?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) } diff --git a/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs b/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs new file mode 100644 index 0000000000..6b9650ccdb --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs @@ -0,0 +1,103 @@ +use crate::old_config_converter::layout_yaml_to_layout_kdl; +use std::path::PathBuf; +use std::{fs::File, io::prelude::*}; +use insta::assert_snapshot; + +#[test] +fn properly_convert_default_layout() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_with_session_name() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_with_session_name_and_attach_false() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_with_config() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_with_config_and_session_name() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_example_1() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_example_2() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_example_3() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/run_htop_layout.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} + +#[test] +fn properly_convert_layout_example_4() -> Result<(), String> { + let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml", env!("CARGO_MANIFEST_DIR"))); + let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; + let mut raw_config_file = String::new(); + handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; + assert_snapshot!(format!("{}", kdl_config)); + Ok(()) +} diff --git a/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml b/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml new file mode 100644 index 0000000000..04fca4a51a --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml @@ -0,0 +1,90 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + borderless: true + - direction: Vertical + body: true + - direction: Vertical + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" + borderless: true + +tabs: +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Horizontal + split_size: + Percent: 50 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical +- direction: Vertical +- direction: Vertical +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 20 + run: + plugin: + location: "zellij:strider" + - direction: Horizontal + split_size: + Percent: 80 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 40 + - direction: Horizontal + split_size: + Percent: 60 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 diff --git a/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml b/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml new file mode 100644 index 0000000000..1449aa7194 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml @@ -0,0 +1,93 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + borderless: true + - direction: Vertical + body: true + - direction: Vertical + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" + borderless: true + +tabs: +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + run: + command: {cmd: htop} + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Horizontal + split_size: + Percent: 50 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical +- direction: Vertical + run: + command: {cmd: htop, args: ["-C"]} +- direction: Vertical +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 20 + run: + plugin: + location: "zellij:strider" + - direction: Horizontal + split_size: + Percent: 80 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 +- direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 40 + - direction: Horizontal + split_size: + Percent: 60 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml new file mode 100644 index 0000000000..0688e54bbc --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml @@ -0,0 +1,22 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" +tabs: + - direction: Vertical diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml new file mode 100644 index 0000000000..0d5f40b966 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml @@ -0,0 +1,34 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" +tabs: + - direction: Vertical +default_shell: fish +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml new file mode 100644 index 0000000000..7e6fa2cb13 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml @@ -0,0 +1,36 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" +tabs: + - direction: Vertical +session: + name: foo +default_shell: fish +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'n',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml new file mode 100644 index 0000000000..9e452574a8 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml @@ -0,0 +1,24 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" +tabs: + - direction: Vertical +session: + name: foo diff --git a/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml new file mode 100644 index 0000000000..1f33f1c3e3 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml @@ -0,0 +1,25 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + - direction: Vertical + body: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" +tabs: + - direction: Vertical +session: + name: foo + attach: false diff --git a/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml b/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml new file mode 100644 index 0000000000..91f4f63cbe --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml @@ -0,0 +1,21 @@ +--- +tabs: + - direction: Vertical + parts: + - direction: Horizontal + split_size: + Percent: 50 + parts: + - direction: Vertical + split_size: + Percent: 50 + - direction: Vertical + split_size: + Percent: 50 + run: + command: {cmd: htop} + - direction: Horizontal + split_size: + Percent: 50 + run: + command: {cmd: htop} diff --git a/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml b/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml new file mode 100644 index 0000000000..05d2798f71 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml @@ -0,0 +1,35 @@ +--- +tabs: + - direction: Horizontal + parts: + - direction: Vertical + split_size: + Fixed: 1 + run: + plugin: + location: "zellij:tab-bar" + borderless: true + - direction: Vertical + parts: + - direction: Vertical + parts: + - direction: Vertical + split_size: + Percent: 50 + run: + command: {cmd: htop} + - direction: Vertical + split_size: + Percent: 50 + run: + command: {cmd: htop, args: ["-C", "--tree"]} + - direction: Vertical + split_size: + Fixed: 5 + - direction: Vertical + split_size: + Fixed: 2 + run: + plugin: + location: "zellij:status-bar" + borderless: true diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_default_layout.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_default_layout.snap new file mode 100644 index 0000000000..94a9a4f320 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_default_layout.snap @@ -0,0 +1,14 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 13 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_1.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_1.snap new file mode 100644 index 0000000000..67cb8a60b5 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_1.snap @@ -0,0 +1,61 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 68 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + default_tab_template { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + children + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="50%" + pane size="50%" + } + } + tab + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="50%" + pane size="50%" + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="50%" + pane size="50%" split_direction="Horizontal" { + pane size="50%" + pane size="50%" + } + } + } + tab + tab + tab + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="20%" { + plugin location="zellij:strider" + } + pane size="80%" split_direction="Horizontal" { + pane size="50%" + pane size="50%" + } + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="40%" + pane size="60%" split_direction="Horizontal" { + pane size="50%" + pane size="50%" + } + } + } +} diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_2.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_2.snap new file mode 100644 index 0000000000..0c5145a17e --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_2.snap @@ -0,0 +1,60 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 79 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + default_tab_template { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + children + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane command="htop" size="50%" + pane size="50%" + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="50%" + pane size="50%" + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="50%" + pane size="50%" split_direction="Horizontal" { + pane size="50%" + pane size="50%" + } + } + } + tab + tab + tab + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="20%" { + plugin location="zellij:strider" + } + pane size="80%" split_direction="Horizontal" { + pane size="50%" + pane size="50%" + } + } + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="40%" + pane size="60%" split_direction="Horizontal" { + pane size="50%" + pane size="50%" + } + } + } +} diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_3.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_3.snap new file mode 100644 index 0000000000..733e2aff41 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_3.snap @@ -0,0 +1,19 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 90 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + default_tab_template { + children + } + tab split_direction="Vertical" { + pane split_direction="Vertical" { + pane size="50%" split_direction="Horizontal" { + pane size="50%" + pane command="htop" size="50%" + } + pane command="htop" size="50%" + } + } +} diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_4.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_4.snap new file mode 100644 index 0000000000..ed6d2d6dfc --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_4.snap @@ -0,0 +1,27 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 101 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + default_tab_template { + children + } + tab split_direction="Horizontal" { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="Vertical" { + pane split_direction="Vertical" { + pane command="htop" size="50%" + pane command="htop" size="50%" { + args "-C" "--tree" + } + } + } + pane size=5 + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } +} diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config.snap new file mode 100644 index 0000000000..a063d91746 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config.snap @@ -0,0 +1,24 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 46 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + } +} +default_shell "fish" + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config_and_session_name.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config_and_session_name.snap new file mode 100644 index 0000000000..a77bc52506 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config_and_session_name.snap @@ -0,0 +1,26 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 57 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} +session_name "foo" +attach_to_session true +keybinds clear-defaults=true { + normal { + bind "Ctrl g" { SwitchToMode "locked"; } + bind "Ctrl p" { SwitchToMode "pane"; } + bind "Ctrl n" { SwitchToMode "resize"; } + bind "Ctrl t" { SwitchToMode "tab"; } + } +} +default_shell "fish" + diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name.snap new file mode 100644 index 0000000000..7b60725666 --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name.snap @@ -0,0 +1,16 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 24 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} +session_name "foo" +attach_to_session true diff --git a/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name_and_attach_false.snap b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name_and_attach_false.snap new file mode 100644 index 0000000000..4a09faeade --- /dev/null +++ b/zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name_and_attach_false.snap @@ -0,0 +1,16 @@ +--- +source: zellij-client/src/old_config_converter/./unit/convert_layout_tests.rs +assertion_line: 35 +expression: "format!(\"{}\", kdl_config)" +--- +layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} +session_name "foo" +attach_to_session false diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 7b72432518..17c74604c5 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -137,11 +137,18 @@ pub enum Sessions { #[clap(short, long, value_parser, default_missing_value("true"))] floating: Option, }, - #[clap(visible_alias = "co")] - Convert { + ConvertConfig { old_config_file: PathBuf, output: Option, - } + }, + ConvertLayout { + old_layout_file: PathBuf, + output: Option, + }, + ConvertTheme { + old_theme_file: PathBuf, + output: Option, + }, } #[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 0fc41c1edb..62b56985f6 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -530,7 +530,7 @@ fn layout_with_default_tab_template() { layout { default_tab_template { pane - pane + children pane } tab name="my first tab" split_direction="Vertical" { diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap index 92dfd4418d..f61145b4f3 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 551 +assertion_line: 549 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -10,7 +10,7 @@ Layout { "my first tab", ), PaneLayout { - children_split_direction: Vertical, + children_split_direction: Horizontal, name: None, children: [ PaneLayout { @@ -23,6 +23,37 @@ Layout { focus: None, external_children_index: None, }, + PaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, PaneLayout { children_split_direction: Horizontal, name: None, @@ -59,6 +90,37 @@ Layout { focus: None, external_children_index: None, }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, PaneLayout { children_split_direction: Horizontal, name: None, @@ -82,7 +144,38 @@ Layout { PaneLayout { children_split_direction: Horizontal, name: None, - children: [], + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], split_size: None, run: None, borderless: false, diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 69b0a2d491..3d2a868825 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -491,7 +491,15 @@ impl <'a>KdlLayoutParser <'a> { if !child_panes.is_empty() { return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); } - child_tabs.push(self.parse_tab_node(child)?); + match &self.default_tab_template { + Some(default_tab_template) => { + let default_tab_template = default_tab_template.clone(); + child_tabs.push(self.parse_tab_node_with_template(child, default_tab_template)?); + }, + None => { + child_tabs.push(self.parse_tab_node(child)?); + } + } } else if let Some(tab_template) = self.tab_templates.get(child_name).cloned() { if !child_panes.is_empty() { return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); From eb63018bb4daf69bcb046b55b1ba661ac40dab2d Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 26 Sep 2022 18:26:16 +0200 Subject: [PATCH 38/55] feat(kdl): pretty errors --- Cargo.lock | 51 +- Cargo.toml | 1 + src/commands.rs | 11 +- .../src/tab/unit/tab_integration_tests.rs | 2 +- zellij-server/src/unit/screen_tests.rs | 9 +- ...i_new_tab_action_with_name_and_layout.snap | 119 ++-- zellij-utils/Cargo.toml | 3 +- zellij-utils/src/input/actions.rs | 33 +- zellij-utils/src/input/config.rs | 93 ++- zellij-utils/src/input/layout.rs | 31 +- zellij-utils/src/input/unit/layout_test.rs | 126 ++-- ..._error_received_on_unknown_input_mode.snap | 2 +- ...r_received_on_unknown_key_instruction.snap | 4 +- ...t__error_on_more_than_one_focused_tab.snap | 4 +- ...rror_on_multiple_layout_nodes_in_file.snap | 4 +- ...rror_on_pane_templates_without_a_name.snap | 4 +- ...error_on_tab_templates_without_a_name.snap | 4 +- ...ut_test__error_on_unknown_layout_node.snap | 4 +- ...error_on_unknown_layout_pane_property.snap | 4 +- ...unknown_layout_pane_template_property.snap | 4 +- ..._error_on_unknown_layout_tab_property.snap | 4 +- ..._unknown_layout_tab_template_property.snap | 4 +- zellij-utils/src/kdl/kdl_layout_parser.rs | 126 ++-- zellij-utils/src/kdl/mod.rs | 666 ++++++++++++------ zellij-utils/src/setup.rs | 6 +- 25 files changed, 786 insertions(+), 533 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2db0fa2b84..ad8705d624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,12 +359,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "chumsky" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" - [[package]] name = "clap" version = "3.2.4" @@ -1233,42 +1227,15 @@ dependencies = [ [[package]] name = "kdl" -version = "4.3.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0170e808d03465c564754ac497fe718aae33146b135a0f882a7ca4066257e0b" +checksum = "a8388a371e0e2ede18bbd94e476fcd45b4ac65cefcedf0c06fd13bd8389574a6" dependencies = [ - "miette 4.7.1", + "miette 5.3.0", "nom 7.1.1", "thiserror", ] -[[package]] -name = "knuffel" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9f7a07459e9dc5d07f5dabfc2c2a965bf39195451eb785b61460c65f386eef" -dependencies = [ - "base64", - "chumsky", - "knuffel-derive", - "miette 4.7.1", - "thiserror", - "unicode-width", -] - -[[package]] -name = "knuffel-derive" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcbdb0b6f26a4e5ecb0dd9074a430398a41b2c1624c205bcc202541ddc15488" -dependencies = [ - "heck 0.4.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1469,11 +1436,11 @@ dependencies = [ [[package]] name = "miette" -version = "4.7.1" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c" +checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d" dependencies = [ - "miette-derive 4.7.1", + "miette-derive 5.3.0", "once_cell", "thiserror", "unicode-width", @@ -1492,9 +1459,9 @@ dependencies = [ [[package]] name = "miette-derive" -version = "4.7.1" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c" +checksum = "4f2485ed7d1fe80704928e3eb86387439609bd0c6bb96db8208daa364cfd1e09" dependencies = [ "proc-macro2", "quote", @@ -3400,6 +3367,7 @@ dependencies = [ "rand 0.8.5", "ssh2", "suggest", + "thiserror", "zellij-client", "zellij-server", "zellij-utils", @@ -3482,7 +3450,6 @@ dependencies = [ "insta", "interprocess", "kdl", - "knuffel", "lazy_static", "libc", "log", diff --git a/Cargo.toml b/Cargo.toml index c0ec182d9a..f0b49a297b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ rust-version = "1.59" [dependencies] anyhow = "1.0" +thiserror = "1.0.30" names = { version = "0.13.0", default-features = false } miette = { version = "3.3.0", features = ["fancy"] } zellij-client = { path = "zellij-client/", version = "0.32.0" } diff --git a/src/commands.rs b/src/commands.rs index f0b8ee9761..fd0cbd2bab 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -6,10 +6,11 @@ use crate::sessions::{ session_exists, ActiveSession, SessionNameMatch, }; use dialoguer::Confirm; -use miette::Result; +use miette::{Result, Report}; use std::path::PathBuf; use std::process; use zellij_utils::input::actions::Action; +use zellij_utils::input::config::ConfigError; use zellij_client::start_client as start_client_impl; use zellij_client::old_config_converter::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl}; use zellij_client::{os_input_output::get_client_os_input, ClientInfo}; @@ -239,6 +240,7 @@ fn attach_with_cli_client(cli_action: zellij_utils::cli::CliAction, session_name std::process::exit(0); } Err(e) => { + eprintln!("{}", e); log::error!("Error sending action: {}", e); std::process::exit(2); } @@ -314,7 +316,12 @@ pub(crate) fn start_client(opts: CliArgs) { let (config, layout, config_options) = match Setup::from_cli_args(&opts) { Ok(results) => results, Err(e) => { - eprintln!("{}", e); + if let ConfigError::KdlError(error) = e { + let report: Report = error.into(); + eprintln!("{:?}", report); + } else { + eprintln!("{}", e); + } process::exit(1); }, }; diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 30903dd418..06149ca536 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -179,7 +179,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); - let layout = Layout::from_str(layout).unwrap(); + let layout = Layout::from_str(layout, "layout_file_name".into()).unwrap(); let tab_layout = layout.new_tab(); let mut tab = Tab::new( index, diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 765af7a4e5..f5d85aa62c 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1979,7 +1979,14 @@ pub fn send_cli_new_tab_action_with_name_and_layout() { send_cli_action_to_server(&session_metadata, new_tab_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![pty_thread, screen_thread]); - assert_snapshot!(format!("{:#?}", *received_pty_instructions.lock().unwrap())); + let new_tab_instruction = received_pty_instructions.lock().unwrap().iter().find(|i| { + if let PtyInstruction::NewTab(..) = i { + return true + } else { + return false + } + }).unwrap().clone(); + assert_snapshot!(format!("{:#?}", new_tab_instruction)); } #[test] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap index f39faf4d52..5f676ab754 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap @@ -1,74 +1,55 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1928 -expression: "format!(\"{:#?}\", * received_pty_instructions.lock().unwrap())" +assertion_line: 1989 +expression: "format!(\"{:#?}\", new_tab_instruction)" --- -[ - NewTab( - None, - Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ), - Some( - "my-awesome-tab-name", - ), - 10, +NewTab( + None, + Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, ), - UpdateActivePane( - Some( - Terminal( - 0, - ), - ), - 1, + Some( + "my-awesome-tab-name", ), - UpdateActivePane( - Some( - Terminal( - 0, - ), - ), - 1, - ), - Exit, -] + 10, +) diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 62831c160e..02fad102ed 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -35,8 +35,7 @@ unicode-width = "0.1.8" miette = { version = "3.3.0", features = ["fancy"] } regex = "1.5.5" tempfile = "3.2.0" -knuffel = "2.0.0" -kdl = "4.3.0" +kdl = { version = "4.5.0", features = ["span"] } #[cfg(not(target_family = "wasm"))] [target.'cfg(not(target_family = "wasm"))'.dependencies] diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 9b729b8b2f..6d85dadd5d 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -5,6 +5,8 @@ use super::layout::{Layout, PaneLayout}; use crate::cli::CliAction; use crate::data::InputMode; use crate::input::options::OnForceClose; +use crate::input::config::{ConfigError, KdlError}; +use miette::{NamedSource, Report}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -304,8 +306,35 @@ impl Action { CliAction::SearchPrevious => Ok(vec![Action::Search(SearchDirection::Up)]), CliAction::NewTab { name, layout } => { if let Some(layout_path) = layout { - let raw_layout = Layout::stringified_from_path_or_default(Some(&layout_path), None).map_err(|e| format!("Failed to load layout: {}", e))?; - let layout = Layout::from_str(&raw_layout).map_err(|e| format!("Failed to parse layout: {}", e))?; + let (path_to_raw_layout, raw_layout) = Layout::stringified_from_path_or_default(Some(&layout_path), None).map_err(|e| format!("Failed to load layout: {}", e))?; + // let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| format!("Failed to parse layout: {}", e))?; + let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| { + let stringified_error = match e { + ConfigError::KdlError(kdl_error) => { + let error = kdl_error.add_src(layout_path.as_path().as_os_str().to_string_lossy().to_string(), String::from(raw_layout)); + let report: Report = error.into(); + format!("{:?}", report) + } + ConfigError::KdlDeserializationError(kdl_error) => { + let error_message = match kdl_error.kind { + kdl::KdlErrorKind::Context("valid node terminator") => { + format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") + }, + _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), + }; + let kdl_error = KdlError { + error_message, + src: Some(NamedSource::new(layout_path.as_path().as_os_str().to_string_lossy().to_string(), String::from(raw_layout))), + offset: Some(kdl_error.span.offset()), + len: Some(kdl_error.span.len()), + }; + let report: Report = kdl_error.into(); + format!("{:?}", report) + }, + e => format!("{}", e) + }; + stringified_error + })?; let mut tabs = layout.tabs(); if tabs.len() > 1 { return Err(format!("Tab layout cannot itself have tabs")); diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 95c6eb863e..d339dc7276 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -3,6 +3,7 @@ use std::io::{self, Read}; use std::path::PathBuf; use thiserror::Error; use crate::data::Palette; +use miette::{Diagnostic, NamedSource, SourceCode, LabeledSpan}; use std::convert::TryFrom; @@ -30,12 +31,63 @@ pub struct Config { } #[derive(Error, Debug)] +pub struct KdlError { + pub error_message: String, + pub src: Option, + pub offset: Option, + pub len: Option, +} + +impl KdlError { + pub fn new_with_location(error_message: String, offset: usize, len: usize) -> Self { + KdlError { + error_message, + src: None, + offset: Some(offset), + len: Some(len), + } + } + pub fn add_src(mut self, src_name: String, src_input: String) -> Self { + self.src = Some(NamedSource::new(src_name, src_input)); + self + } +} + +impl std::fmt::Display for KdlError{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "Failed to parse Zellij configuration") + } +} +use std::fmt::Display; + +impl Diagnostic for KdlError { + fn source_code(&self) -> Option<&dyn SourceCode> { + match self.src.as_ref() { + Some(src) => Some(src), + None => None + } + } + fn help<'a>(&'a self) -> Option> { + // TODO: link to specific relevant sections + Some(Box::new(format!("For more information, please see our configuration guide: https://zellij.dev/documentation/configuration.html"))) + } + fn labels(&self) -> Option + '_>> { + if let (Some(offset), Some(len)) = (self.offset, self.len) { + let label = LabeledSpan::new(Some(self.error_message.clone()), offset, len); + Some(Box::new(std::iter::once(label))) + } else { + None + } + } +} + +#[derive(Error, Debug, Diagnostic)] pub enum ConfigError { // Deserialization error #[error("Deserialization error: {0}")] KdlDeserializationError(#[from] kdl::KdlError), #[error("KdlDeserialization error: {0}")] - KdlParsingError(String), + KdlError(KdlError), // TODO: consolidate these // Io error #[error("IoError: {0}")] Io(#[from] io::Error), @@ -54,6 +106,17 @@ pub enum ConfigError { ConversionError(#[from] ConversionError), } +impl ConfigError { + pub fn new_kdl_error(error_message: String, offset: usize, len: usize) -> Self { + ConfigError::KdlError(KdlError { + error_message, + src: None, + offset: Some(offset), + len: Some(len), + }) + } +} + #[derive(Debug, Error)] pub enum ConversionError { #[error("{0}")] @@ -104,7 +167,11 @@ impl Config { /// Gets default configuration from assets pub fn from_default_assets() -> ConfigResult { let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec())?; - Self::from_kdl(&cfg, None) + match Self::from_kdl(&cfg, None) { + Ok(config) => Ok(config), + Err(ConfigError::KdlError(kdl_error)) => Err(ConfigError::KdlError(kdl_error.add_src("Default built-in-configuration".into(), cfg))), + Err(e) => Err(e) + } } pub fn from_path(path: &PathBuf, default_config: Option) -> ConfigResult { match File::open(path) { @@ -112,7 +179,27 @@ impl Config { let mut kdl_config = String::new(); file.read_to_string(&mut kdl_config) .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; - Config::from_kdl(&kdl_config, default_config) + match Config::from_kdl(&kdl_config, default_config) { + Ok(config) => Ok(config), + Err(ConfigError::KdlDeserializationError(kdl_error)) => { + println!("kdl_error.code: {:?}", kdl_error.kind); + let error_message = match kdl_error.kind { + kdl::KdlErrorKind::Context("valid node terminator") => { + format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") + }, + _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), + }; + let kdl_error = KdlError { + error_message, + src: Some(NamedSource::new(path.as_path().as_os_str().to_string_lossy(), kdl_config)), + offset: Some(kdl_error.span.offset()), + len: Some(kdl_error.span.len()), + }; + Err(ConfigError::KdlError(kdl_error)) + } + Err(ConfigError::KdlError(kdl_error)) => Err(ConfigError::KdlError(kdl_error.add_src(path.as_path().as_os_str().to_string_lossy().to_string(), kdl_config))), + Err(e) => Err(e) + } }, Err(e) => Err(ConfigError::IoPath(e, path.into())), } diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 67087b70c2..6ea4c48e84 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -171,7 +171,6 @@ impl PaneLayout { run_instructions } pub fn with_one_pane() -> Self { - // TODO: do we need this? let mut default_layout = PaneLayout::default(); default_layout.children = vec![PaneLayout::default()]; default_layout @@ -202,7 +201,7 @@ impl LayoutParts { Ok(()) }, LayoutParts::Tabs(_tabs) => { - Err(ConfigError::KdlParsingError("Trying to insert a pane into a tab layout".into())) + Err(ConfigError::new_kdl_error("Trying to insert a pane into a tab layout".into(), 0, 0)) } } } @@ -215,7 +214,7 @@ impl Default for LayoutParts { } impl Layout { - pub fn stringified_from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result { + pub fn stringified_from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) match layout_path { Some(layout_path) => { // The way we determine where to look for the layout is similar to @@ -238,20 +237,18 @@ impl Layout { } } pub fn from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option, config: Config) -> Result<(Layout, Config), ConfigError> { - let raw_layout = Layout::stringified_from_path_or_default(layout_path, layout_dir)?; - let kdl_layout: KdlDocument = raw_layout.parse()?; - let layout = Layout::from_kdl(&kdl_layout)?; + let (path_to_raw_layout, raw_layout) = Layout::stringified_from_path_or_default(layout_path, layout_dir)?; + let layout = Layout::from_kdl(&raw_layout, path_to_raw_layout)?; let config = Config::from_kdl(&raw_layout, Some(config))?; // this merges the two config, with Ok((layout, config)) } - pub fn from_str(raw: &str) -> Result { - let kdl_layout: KdlDocument = raw.parse()?; - Layout::from_kdl(&kdl_layout) + pub fn from_str(raw: &str, path_to_raw_layout: String) -> Result { + Layout::from_kdl(raw, path_to_raw_layout) } pub fn stringified_from_dir( layout: &PathBuf, layout_dir: Option<&PathBuf>, - ) -> Result { + ) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) match layout_dir { Some(dir) => { let layout_path = &dir.join(layout); @@ -264,24 +261,24 @@ impl Layout { None => Layout::stringified_from_default_assets(layout), } } - pub fn stringified_from_path(layout_path: &Path) -> Result { + pub fn stringified_from_path(layout_path: &Path) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) let mut layout_file = File::open(&layout_path) .or_else(|_| File::open(&layout_path.with_extension("kdl"))) .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; let mut kdl_layout = String::new(); layout_file.read_to_string(&mut kdl_layout)?; - Ok(kdl_layout) + Ok((layout_path.as_os_str().to_string_lossy().into(), kdl_layout)) } - pub fn stringified_from_default_assets(path: &Path) -> Result { + pub fn stringified_from_default_assets(path: &Path) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) // TODO: ideally these should not be hard-coded // we should load layouts by name from the config // and load them from a hashmap or some such match path.to_str() { - Some("default") => Self::stringified_default_from_assets(), - Some("strider") => Self::stringified_strider_from_assets(), - Some("disable-status-bar") => Self::stringified_disable_status_from_assets(), - Some("compact") => Self::stringified_compact_from_assets(), + Some("default") => Ok(("Default layout".into(), Self::stringified_default_from_assets()?)), + Some("strider") => Ok(("Strider layout".into(), Self::stringified_strider_from_assets()?)), + Some("disable-status-bar") => Ok(("Disable Status Bar layout".into(), Self::stringified_disable_status_from_assets()?)), + Some("compact") => Ok(("Compact layout".into(), Self::stringified_compact_from_assets()?)), None | Some(_) => Err(ConfigError::IoPath( std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), path.into(), diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 62b56985f6..b8457a74bb 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -4,8 +4,7 @@ use insta::assert_snapshot; #[test] fn empty_layout() { let kdl_layout = "layout"; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout::default()), ..Default::default() @@ -20,8 +19,7 @@ fn layout_with_one_pane() { pane } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![PaneLayout::default()], @@ -41,8 +39,7 @@ fn layout_with_multiple_panes() { pane } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -71,8 +68,7 @@ fn layout_with_nested_panes() { } } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -106,8 +102,7 @@ fn layout_with_tabs() { tab } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ (None, PaneLayout::default()), @@ -133,8 +128,7 @@ fn layout_with_nested_differing_tabs() { } } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ (None, PaneLayout { @@ -171,8 +165,7 @@ fn layout_with_panes_in_different_mixed_split_sizes() { pane size=2; } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -207,8 +200,7 @@ fn layout_with_command_panes() { pane command="htop" } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -234,8 +226,7 @@ fn layout_with_command_panes_and_cwd() { pane command="htop" cwd="/path/to/my/cwd" } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some( PaneLayout { @@ -266,8 +257,7 @@ fn layout_with_command_panes_and_cwd_and_args() { } } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some( PaneLayout { @@ -302,8 +292,7 @@ fn layout_with_plugin_panes() { } } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -336,8 +325,7 @@ fn layout_with_borderless_panes() { pane borderless=true } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -360,8 +348,7 @@ fn layout_with_focused_panes() { pane focus=true } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -384,8 +371,7 @@ fn layout_with_pane_names() { pane name="my awesome pane" } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { children: vec![ @@ -409,8 +395,7 @@ fn layout_with_tab_names() { tab name="my cool tab name 2" } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ (Some("my cool tab name 1".into()), PaneLayout { @@ -437,8 +422,7 @@ fn layout_with_focused_tab() { tab } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ (None, PaneLayout::default()), @@ -472,8 +456,7 @@ fn layout_with_tab_templates() { one-above-one-below } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ (Some("my first tab".into()), PaneLayout { @@ -544,8 +527,7 @@ fn layout_with_default_tab_template() { tab } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -572,8 +554,7 @@ fn layout_with_pane_templates() { left-and-right } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -594,8 +575,7 @@ fn layout_with_tab_and_pane_templates() { left-right-and-htop } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -616,8 +596,7 @@ fn layout_with_nested_pane_templates() { left-and-right } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -643,8 +622,7 @@ fn layout_with_nested_branched_pane_templates() { left-and-right } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -668,8 +646,7 @@ fn circular_dependency_pane_templates_error() { one } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "circular dependency detected"); } @@ -691,8 +668,7 @@ fn children_not_as_first_child_of_tab_template() { horizontal-with-vertical-top } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -715,8 +691,7 @@ fn error_on_more_than_one_children_block_in_tab_template() { horizontal-with-vertical-top } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "error provided for more than one children block"); } @@ -738,8 +713,7 @@ fn children_not_as_first_child_of_pane_template() { horizontal-with-vertical-top } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -762,8 +736,7 @@ fn error_on_more_than_one_children_block_in_pane_template() { horizontal-with-vertical-top } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "error provided for more than one children block"); } @@ -789,8 +762,7 @@ fn combined_tab_and_pane_template_both_with_children() { horizontal-with-vertical-top } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -806,8 +778,7 @@ fn cannot_define_tab_template_name_with_space() { pane } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "error provided for tab name with space"); } @@ -823,8 +794,7 @@ fn cannot_define_pane_template_name_with_space() { pane } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "error provided for tab name with space"); } @@ -838,8 +808,7 @@ fn cannot_define_panes_and_tabs_on_same_level() { } } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "error provided for tab and pane on the same level"); } @@ -867,8 +836,7 @@ fn cannot_define_tab_template_names_as_keywords() { pane }} ", keyword); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "{}", format!("error provided for tab template name with keyword: {}", keyword)); } } @@ -896,8 +864,7 @@ fn cannot_define_pane_template_names_as_keywords() { pane }} ", keyword); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout = Layout::from_kdl(&kdl_layout); + let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into()); assert!(layout.is_err(), "{}", format!("error provided for pane template name with keyword: {}", keyword)); } } @@ -908,8 +875,7 @@ fn error_on_multiple_layout_nodes_in_file() { layout layout "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -922,8 +888,7 @@ fn error_on_unknown_layout_node() { pane }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -934,8 +899,7 @@ fn error_on_unknown_layout_pane_property() { pane spit_size=1 }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -946,8 +910,7 @@ fn error_on_unknown_layout_pane_template_property() { pane_template name=\"my_cool_template\" spit_size=1 }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -958,8 +921,7 @@ fn error_on_unknown_layout_tab_property() { tab spit_size=1 }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -970,8 +932,7 @@ fn error_on_unknown_layout_tab_template_property() { tab_template name=\"my_cool_template\" spit_size=1 }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -986,8 +947,7 @@ fn error_on_pane_templates_without_a_name() { }} }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1002,8 +962,7 @@ fn error_on_tab_templates_without_a_name() { }} }} "); - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1016,7 +975,6 @@ fn error_on_more_than_one_focused_tab() { tab } "#; - let kdl_layout: KdlDocument = kdl_layout.parse().unwrap(); - let layout_error = Layout::from_kdl(&kdl_layout).unwrap_err(); + let layout_error = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap index c001b5f3e4..eab0dc6f46 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap @@ -3,4 +3,4 @@ source: zellij-utils/src/input/./unit/keybinds_test.rs assertion_line: 476 expression: "format!(\"{:?}\", config_error)" --- -ConversionError(UnknownInputMode("i_do_not_exist")) +KdlError(KdlError { error_message: "Invalid mode: 'i_do_not_exist'", src: None, offset: Some(32), len: Some(14) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap index 6d7a81048a..cce4254ad3 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/keybinds_test.rs -assertion_line: 489 +assertion_line: 490 expression: "format!(\"{:?}\", config_error)" --- -KdlParsingError("Unknown keybind instruction: 'i_am_not_bind_or_unbind'") +KdlError(KdlError { error_message: "Unknown keybind instruction: 'i_am_not_bind_or_unbind'", src: None, offset: Some(55), len: Some(23) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap index 53e1546f68..b640aa4307 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1078 +assertion_line: 979 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Only one tab can be focused") +KdlError(KdlError { error_message: "Only one tab can be focused", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(0), len: Some(98) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap index 508020f1ce..0c2811ec7f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 946 +assertion_line: 879 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Only one layout node per file allowed") +KdlError(KdlError { error_message: "Only one layout node per file allowed", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(0), len: Some(31) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap index 8e63fd7f05..cf7fc6923c 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1000 +assertion_line: 951 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Pane templates must have a name") +KdlError(KdlError { error_message: "Pane templates must have a name", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(96) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap index dc2c3f1c3d..c7b591f8eb 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1016 +assertion_line: 966 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Tab templates must have a name") +KdlError(KdlError { error_message: "Tab templates must have a name", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(95) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap index af09d556ee..b2b28ffef8 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 960 +assertion_line: 892 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Unknown layout node: 'i_am_not_a_proper_node'") +KdlError(KdlError { error_message: "Unknown layout node: 'i_am_not_a_proper_node'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(47), len: Some(22) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap index 5df1eebf24..b856564086 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 972 +assertion_line: 903 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Invalid pane property 'spit_size'") +KdlError(KdlError { error_message: "Invalid pane property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(16) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap index 01b8c275c8..404f988cd2 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 984 +assertion_line: 914 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Invalid pane property 'spit_size'") +KdlError(KdlError { error_message: "Invalid pane property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(49) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap index 9603a98398..bbe1769c4f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 996 +assertion_line: 925 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Invalid tab property 'spit_size'") +KdlError(KdlError { error_message: "Invalid tab property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(15) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap index b638b297c7..134970f7f0 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1008 +assertion_line: 936 expression: "format!(\"{:?}\", layout_error)" --- -KdlParsingError("Invalid tab property 'spit_size'") +KdlError(KdlError { error_message: "Invalid tab property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(48) }) diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 3d2a868825..4d7581769f 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -10,14 +10,20 @@ use std::str::FromStr; use std::collections::{HashMap, HashSet}; use crate::{ + kdl_parsing_error, kdl_string_arguments, kdl_children_nodes, kdl_name, kdl_get_string_entry, kdl_get_child, kdl_get_bool_property_or_child_value, + kdl_get_bool_property_or_child_value_with_error, kdl_get_string_property_or_child_value, + kdl_get_string_property_or_child_value_with_error, + kdl_get_property_or_child, + kdl_child_with_name, kdl_get_int_property_or_child_value, + kdl_property_or_child_value_node, kdl_property_names, }; @@ -27,16 +33,18 @@ use std::path::PathBuf; use url::Url; pub struct KdlLayoutParser <'a>{ - kdl_layout: &'a KdlDocument, + raw_layout: &'a str, + file_name: String, tab_templates: HashMap, pane_templates: HashMap, default_tab_template: Option, } impl <'a>KdlLayoutParser <'a> { - pub fn new(kdl_layout: &'a KdlDocument) -> Self { + pub fn new(raw_layout: &'a str, file_name: String) -> Self { KdlLayoutParser { - kdl_layout, + raw_layout, + file_name, tab_templates: HashMap::new(), pane_templates: HashMap::new(), default_tab_template: None, @@ -69,11 +77,11 @@ impl <'a>KdlLayoutParser <'a> { property_name == "name" || property_name == "split_direction" } - fn assert_legal_node_name(&self, name: &str) -> Result<(), ConfigError> { + fn assert_legal_node_name(&self, name: &str, kdl_node: &KdlNode) -> Result<(), ConfigError> { if name.contains(char::is_whitespace) { - Err(ConfigError::KdlParsingError(format!("Node names ({}) cannot contain whitespace.", name))) + Err(ConfigError::new_kdl_error(format!("Node names ({}) cannot contain whitespace.", name), kdl_node.span().offset(), kdl_node.span().len())) } else if self.is_a_reserved_word(&name) { - Err(ConfigError::KdlParsingError(format!("Node name '{}' is a reserved word.", name))) + Err(ConfigError::new_kdl_error(format!("Node name '{}' is a reserved word.", name), kdl_node.span().offset(), kdl_node.span().len())) } else { Ok(()) } @@ -83,14 +91,20 @@ impl <'a>KdlLayoutParser <'a> { Ok(Some(SplitSize::from_str(size)?)) } else if let Some(size) = kdl_get_int_property_or_child_value!(kdl_node, "size") { Ok(Some(SplitSize::Fixed(size as usize))) + } else if let Some(node) = kdl_property_or_child_value_node!(kdl_node, "size") { + Err(kdl_parsing_error!(format!("size should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")"), node)) + } else if let Some(node) = kdl_child_with_name!(kdl_node, "size") { + println!("can has child node?"); + Err(kdl_parsing_error!(format!("size cannot be bare, it should have a value (eg. 'size 1', or 'size \"50%\"')"), node)) } else { Ok(None) } } fn parse_plugin_block(&self, plugin_block: &KdlNode) -> Result, ConfigError> { - let _allow_exec_host_cmd = kdl_get_bool_property_or_child_value!(plugin_block, "_allow_exec_host_cmd").unwrap_or(false); - let string_url = kdl_get_string_property_or_child_value!(plugin_block, "location").ok_or(ConfigError::KdlParsingError("Plugins must have a location".into()))?; - let url = Url::parse(string_url).map_err(|e| ConfigError::KdlParsingError(format!("Failed to aprse url: {:?}", e)))?; + let _allow_exec_host_cmd = kdl_get_bool_property_or_child_value_with_error!(plugin_block, "_allow_exec_host_cmd").unwrap_or(false); + let string_url = kdl_get_string_property_or_child_value_with_error!(plugin_block, "location").ok_or(ConfigError::new_kdl_error("Plugins must have a location".into(), plugin_block.span().offset(), plugin_block.span().len()))?; + let url_node = kdl_get_property_or_child!(plugin_block, "location").ok_or(ConfigError::new_kdl_error("Plugins must have a location".into(), plugin_block.span().offset(), plugin_block.span().len()))?; + let url = Url::parse(string_url).map_err(|e| ConfigError::new_kdl_error(format!("Failed to parse url: {:?}", e), url_node.span().offset(), url_node.span().len()))?; let location = RunPluginLocation::try_from(url)?; Ok(Some(Run::Plugin(RunPlugin { _allow_exec_host_cmd, @@ -98,18 +112,23 @@ impl <'a>KdlLayoutParser <'a> { }))) } fn parse_pane_command(&self, pane_node: &KdlNode) -> Result, ConfigError> { - let command = kdl_get_string_property_or_child_value!(pane_node, "command").map(|c| PathBuf::from(c)); - let cwd = kdl_get_string_property_or_child_value!(pane_node, "cwd").map(|c| PathBuf::from(c)); + let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command").map(|c| PathBuf::from(c)); + let cwd = kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd").map(|c| PathBuf::from(c)); let args = match kdl_get_child!(pane_node, "args") { - Some(kdl_args) => Some(kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect()), + Some(kdl_args) => { + if kdl_args.entries().is_empty() { + return Err(kdl_parsing_error!(format!("args cannot be empty and should contain one or more command arguments (eg. args \"-h\" \"-v\")"), kdl_args)); + } + Some(kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect()) + } None => None, }; match (command, cwd, args) { - (None, Some(_cwd), _) => { - Err(ConfigError::KdlParsingError("Cwd can only be set if a command was specified".into())) + (None, Some(cwd), _) => { + Err(ConfigError::new_kdl_error("Cwd can only be set if a command was specified".into(), pane_node.span().offset(), pane_node.span().offset())) } (None, _, Some(_args)) => { - Err(ConfigError::KdlParsingError("Args can only be set if a command was specified".into())) + Err(ConfigError::new_kdl_error("Args can only be set if a command was specified".into(), pane_node.span().offset(), pane_node.span().len())) } (Some(command), cwd, args) => { Ok(Some(Run::Command(RunCommand { @@ -125,7 +144,7 @@ impl <'a>KdlLayoutParser <'a> { let mut run = self.parse_pane_command(kdl_node)?; if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") { if run.is_some() { - return Err(ConfigError::KdlParsingError("Cannot have both a command and a plugin block for a single pane".into())); + return Err(ConfigError::new_kdl_error("Cannot have both a command and a plugin block for a single pane".into(), plugin_block.span().offset(), plugin_block.span().len())); } run = self.parse_plugin_block(plugin_block)?; } @@ -133,9 +152,9 @@ impl <'a>KdlLayoutParser <'a> { } fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { self.assert_valid_pane_properties(kdl_node)?; - let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); - let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); - let name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|name| name.to_string()); + let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); + let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name").map(|name| name.to_string()); let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_or_plugin_block(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; @@ -165,8 +184,8 @@ impl <'a>KdlLayoutParser <'a> { children: child_panes, ..Default::default() }; - self.assert_one_children_block(&pane_layout)?; - self.insert_layout_children_or_error(&mut pane_layout, child_panes_layout)?; + self.assert_one_children_block(&pane_layout, kdl_node)?; + self.insert_layout_children_or_error(&mut pane_layout, child_panes_layout, kdl_node)?; }, None => { if let Some(index_of_children) = pane_layout.external_children_index { @@ -183,12 +202,13 @@ impl <'a>KdlLayoutParser <'a> { None => SplitDirection::default(), }) } - fn parse_pane_template_node(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name + fn parse_pane_template_node(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { self.assert_valid_pane_properties(kdl_node)?; - let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()).ok_or(ConfigError::KdlParsingError("Pane templates must have a name".into()))?; - self.assert_legal_node_name(&template_name)?; - let borderless = kdl_get_bool_property_or_child_value!(kdl_node, "borderless"); - let focus = kdl_get_bool_property_or_child_value!(kdl_node, "focus"); + let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()) + .ok_or(ConfigError::new_kdl_error("Pane templates must have a name".into(), kdl_node.span().offset(), kdl_node.span().len()))?; + self.assert_legal_node_name(&template_name, kdl_node)?; + let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_or_plugin_block(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; @@ -254,10 +274,10 @@ impl <'a>KdlLayoutParser <'a> { } Ok((external_children_index, nodes)) } - fn assert_one_children_block(&self, layout: &PaneLayout) -> Result<(), ConfigError> { + fn assert_one_children_block(&self, layout: &PaneLayout, kdl_node: &KdlNode) -> Result<(), ConfigError> { let children_block_count = layout.children_block_count(); if children_block_count != 1 { - return Err(ConfigError::KdlParsingError(format!("Layout has {} children blocks, only 1 is allowed", children_block_count))); + return Err(ConfigError::new_kdl_error(format!("Layout has {} children blocks, only 1 is allowed", children_block_count), kdl_node.span().offset(), kdl_node.span().len())); } Ok(()) } @@ -265,7 +285,7 @@ impl <'a>KdlLayoutParser <'a> { let all_property_names = kdl_property_names!(pane_node); for name in all_property_names { if !self.is_a_valid_pane_property(name) { - return Err(ConfigError::KdlParsingError(format!("Invalid pane property '{}'", name))); + return Err(ConfigError::new_kdl_error(format!("Invalid pane property '{}'", name), pane_node.span().offset(), pane_node.span().len())); } } Ok(()) @@ -274,15 +294,15 @@ impl <'a>KdlLayoutParser <'a> { let all_property_names = kdl_property_names!(pane_node); for name in all_property_names { if !self.is_a_valid_tab_property(name) { - return Err(ConfigError::KdlParsingError(format!("Invalid tab property '{}'", name))); + return Err(ConfigError::new_kdl_error(format!("Invalid tab property '{}'", name), pane_node.span().offset(), pane_node.span().len())); } } Ok(()) } - fn insert_layout_children_or_error(&self, layout: &mut PaneLayout, mut child_panes_layout: PaneLayout) -> Result<(), ConfigError> { + fn insert_layout_children_or_error(&self, layout: &mut PaneLayout, mut child_panes_layout: PaneLayout, kdl_node: &KdlNode) -> Result<(), ConfigError> { let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?; if !successfully_inserted { - Err(ConfigError::KdlParsingError("This tab template does not have children".into())) + Err(ConfigError::new_kdl_error("This tab template does not have children".into(), kdl_node.span().offset(), kdl_node.span().len())) } else { Ok(()) } @@ -302,8 +322,8 @@ impl <'a>KdlLayoutParser <'a> { children: child_panes, ..Default::default() }; - self.assert_one_children_block(&tab_layout)?; - self.insert_layout_children_or_error(&mut tab_layout, child_panes_layout)?; + self.assert_one_children_block(&tab_layout, kdl_node)?; + self.insert_layout_children_or_error(&mut tab_layout, child_panes_layout, kdl_node)?; }, None => { if let Some(index_of_children) = tab_layout.external_children_index { @@ -314,19 +334,20 @@ impl <'a>KdlLayoutParser <'a> { tab_layout.external_children_index = None; Ok((is_focused, tab_name, tab_layout)) } - fn populate_one_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name - let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()).ok_or(ConfigError::KdlParsingError("Tab templates must have a name".into()))?; - self.assert_legal_node_name(&template_name)?; + fn populate_one_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { + let template_name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name").map(|s| s.to_string()) + .ok_or(ConfigError::new_kdl_error("Tab templates must have a name".into(), kdl_node.span().offset(), kdl_node.span().len()))?; + self.assert_legal_node_name(&template_name, kdl_node)?; self.tab_templates.insert(template_name, self.parse_tab_template_node(kdl_node)?); Ok(()) } - fn populate_default_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { // String is the tab name + fn populate_default_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { self.default_tab_template = Some(self.parse_tab_template_node(kdl_node)?); Ok(()) } fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result { self.assert_valid_tab_properties(kdl_node)?; - let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { + let children_split_direction = match kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, None => SplitDirection::default(), }; @@ -368,7 +389,7 @@ impl <'a>KdlLayoutParser <'a> { for child in kdl_children { if kdl_name!(child) == "pane_template" { let template_name = kdl_get_string_property_or_child_value!(child, "name") - .ok_or(ConfigError::KdlParsingError("Pane templates must have a name".into()))?; + .ok_or(ConfigError::new_kdl_error("Pane templates must have a name".into(), child.span().offset(), child.span().len()))?; let mut template_children = HashSet::new(); self.get_pane_template_dependencies(child, &mut template_children)?; dependency_tree.insert(template_name, template_children); @@ -402,7 +423,7 @@ impl <'a>KdlLayoutParser <'a> { }; Ok(()) } - fn populate_pane_templates(&mut self, layout_children: &[KdlNode]) -> Result<(), ConfigError> { + fn populate_pane_templates(&mut self, layout_children: &[KdlNode], kdl_layout: &KdlDocument) -> Result<(), ConfigError> { let mut pane_template_dependency_tree = self.get_pane_template_dependency_tree(layout_children)?; let mut pane_template_names_to_parse: Vec<&str> = vec![]; // toposort the dependency tree so that we parse the pane_templates before their @@ -415,7 +436,7 @@ impl <'a>KdlLayoutParser <'a> { } } if candidates.is_empty() { - return Err(ConfigError::KdlParsingError("Circular dependency detected between pane templates.".into())); + return Err(ConfigError::new_kdl_error("Circular dependency detected between pane templates.".into(), kdl_layout.span().offset(), kdl_layout.span().len())); } for candidate_to_remove in candidates { pane_template_dependency_tree.remove(candidate_to_remove); @@ -484,12 +505,12 @@ impl <'a>KdlLayoutParser <'a> { let child_name = kdl_name!(child); if child_name == "pane" { if !child_tabs.is_empty() { - return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); } child_panes.push(self.parse_pane_node(child)?); } else if child_name == "tab" { if !child_panes.is_empty() { - return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); } match &self.default_tab_template { Some(default_tab_template) => { @@ -502,29 +523,30 @@ impl <'a>KdlLayoutParser <'a> { } } else if let Some(tab_template) = self.tab_templates.get(child_name).cloned() { if !child_panes.is_empty() { - return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); } child_tabs.push(self.parse_tab_node_with_template(child, tab_template)?); } else if let Some(pane_template) = self.pane_templates.get(child_name).cloned() { if !child_tabs.is_empty() { - return Err(ConfigError::KdlParsingError("Cannot have both tabs and panes in the same node".into())); + return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); } child_panes.push(self.parse_pane_node_with_template(child, pane_template)?); } else if !self.is_a_reserved_word(child_name) { - return Err(ConfigError::KdlParsingError(format!("Unknown layout node: '{}'", child_name))); + return Err(ConfigError::new_kdl_error(format!("Unknown layout node: '{}'", child_name), child.span().offset(), child.span().len())); } Ok(()) } pub fn parse(&mut self) -> Result { - let layout_node = self.kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::KdlParsingError("No layout found".into()))?; - let has_multiple_layout_nodes = self.kdl_layout.nodes().iter().filter(|n| kdl_name!(n) == "layout").count() > 1; + let kdl_layout: KdlDocument = self.raw_layout.parse()?; + let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::new_kdl_error("No layout found".into(), kdl_layout.span().offset(), kdl_layout.span().len()))?; + let has_multiple_layout_nodes = kdl_layout.nodes().iter().filter(|n| kdl_name!(n) == "layout").count() > 1; if has_multiple_layout_nodes { - return Err(ConfigError::KdlParsingError("Only one layout node per file allowed".into())); + return Err(ConfigError::new_kdl_error("Only one layout node per file allowed".into(), kdl_layout.span().offset(), kdl_layout.span().len())); } let mut child_tabs = vec![]; let mut child_panes = vec![]; if let Some(children) = kdl_children_nodes!(layout_node) { - self.populate_pane_templates(children)?; + self.populate_pane_templates(children, &kdl_layout)?; self.populate_tab_templates(children)?; for child in children { self.populate_layout_child(child, &mut child_tabs, &mut child_panes)?; @@ -533,7 +555,7 @@ impl <'a>KdlLayoutParser <'a> { if !child_tabs.is_empty() { let has_more_than_one_focused_tab = child_tabs.iter().filter(|(is_focused, _, _)| *is_focused).count() > 1; if has_more_than_one_focused_tab { - return Err(ConfigError::KdlParsingError("Only one tab can be focused".into())); + return Err(ConfigError::new_kdl_error("Only one tab can be focused".into(), kdl_layout.span().offset(), kdl_layout.span().len())); } let focused_tab_index = child_tabs.iter().position(|(is_focused, _, _)| *is_focused); let child_tabs: Vec<(Option, PaneLayout)> = child_tabs.drain(..).map(|(_is_focused, tab_name, pane_layout)| (tab_name, pane_layout)).collect(); diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 6ce9e26267..ada4629aa4 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -5,7 +5,7 @@ use crate::envs::EnvironmentVariables; use crate::input::command::RunCommand; use crate::input::keybinds::Keybinds; use crate::input::layout::{Layout, RunPlugin, RunPluginLocation}; -use crate::input::config::{Config, ConfigError}; +use crate::input::config::{Config, ConfigError, KdlError}; use url::Url; use crate::data::{InputMode, Key, CharOrArrow, PaletteColor, Palette}; use crate::input::options::{Options, OnForceClose, Clipboard}; @@ -15,7 +15,9 @@ use std::io::Read; use crate::input::plugins::{PluginsConfig, PluginConfig, PluginType, PluginTag}; use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; -use kdl::{KdlDocument, KdlValue, KdlNode}; +use miette::NamedSource; + +use kdl::{KdlDocument, KdlValue, KdlNode, KdlEntry}; use std::str::FromStr; use std::path::PathBuf; @@ -25,10 +27,10 @@ use crate::input::command::RunCommandAction; #[macro_export] macro_rules! parse_kdl_action_arguments { - ( $action_name:expr, $action_arguments:expr ) => { + ( $action_name:expr, $action_arguments:expr, $action_node:expr ) => { { if !$action_arguments.is_empty() { - Err(format!("Failed to parse action: {}", $action_name).into()) + Err(ConfigError::new_kdl_error(format!("Action '{}' must have arguments", $action_name), $action_node.span().offset(), $action_node.span().len())) } else { match $action_name { "Quit" => Ok(Action::Quit), @@ -60,7 +62,7 @@ macro_rules! parse_kdl_action_arguments { "Copy" => Ok(Action::Copy), "Confirm" => Ok(Action::Confirm), "Deny" => Ok(Action::Deny), - _ => Err(format!("Error parsing enum variant: {:?}", $action_name).into()) + _ => Err(ConfigError::new_kdl_error(format!("Unsupported action: {:?}", $action_name), $action_node.span().offset(), $action_node.span().len())) } } } @@ -69,19 +71,32 @@ macro_rules! parse_kdl_action_arguments { #[macro_export] macro_rules! parse_kdl_action_u8_arguments { - ( $action_name:expr, $action_arguments:expr ) => {{ + ( $action_name:expr, $action_arguments:expr, $action_node:expr ) => {{ let mut bytes = vec![]; - for kdl_value in $action_arguments.iter() { - match kdl_value.as_i64() { + for kdl_entry in $action_arguments.iter() { + match kdl_entry.value().as_i64() { Some(int_value) => bytes.push(int_value as u8), None => { - return Err(format!("Failed to parse action: {}", $action_name).into()); + return Err(ConfigError::new_kdl_error(format!("Arguments for '{}' must be integers", $action_name), kdl_entry.span().offset(), kdl_entry.span().len())); } } }; - Action::new_from_bytes($action_name, bytes) + Action::new_from_bytes($action_name, bytes, $action_node) }} } + + +#[macro_export] +macro_rules! kdl_parsing_error { + ( $message:expr, $entry:expr ) => { + ConfigError::new_kdl_error( + $message, + $entry.span().offset(), + $entry.span().len() + ) + } +} + #[macro_export] macro_rules! kdl_entries_as_i64 { ( $node:expr ) => { @@ -112,31 +127,43 @@ macro_rules! entry_count { #[macro_export] macro_rules! parse_kdl_action_char_or_string_arguments { - ( $action_name:expr, $action_arguments:expr ) => {{ + ( $action_name:expr, $action_arguments:expr, $action_node:expr ) => {{ let mut chars_to_write = String::new(); - for kdl_value in $action_arguments.iter() { - match kdl_value.as_string() { + for kdl_entry in $action_arguments.iter() { + match kdl_entry.value().as_string() { Some(string_value) => chars_to_write.push_str(string_value), None => { - return Err(format!("Failed to parse action: {}", $action_name).into()); + return Err(ConfigError::new_kdl_error(format!("All entries for action '{}' must be strings", $action_name), kdl_entry.span().offset(), kdl_entry.span().len())) } } }; - Action::new_from_string($action_name, chars_to_write) + Action::new_from_string($action_name, chars_to_write, $action_node) }} } #[macro_export] macro_rules! kdl_arg_is_truthy { ( $kdl_node:expr, $arg_name:expr ) => { - $kdl_node.get($arg_name).and_then(|c| c.value().as_bool()).unwrap_or(false) + match $kdl_node.get($arg_name) { + Some(arg) => { + match arg.value().as_bool() { + Some(value) => value, + None => { + return Err(ConfigError::new_kdl_error(format!("Argument must be true or false, found: {}", arg.value()), arg.span().offset(), arg.span().len())) + } + } + }, + None => { + false + } + } } } #[macro_export] macro_rules! kdl_children_nodes_or_error { ( $kdl_node:expr, $error:expr ) => { - $kdl_node.children().ok_or(ConfigError::KdlParsingError($error.into()))?.nodes() + $kdl_node.children().ok_or(ConfigError::new_kdl_error($error.into(), $kdl_node.span().offset(), $kdl_node.span().len()))?.nodes() } } @@ -150,7 +177,7 @@ macro_rules! kdl_children_nodes { #[macro_export] macro_rules! kdl_children_or_error { ( $kdl_node:expr, $error:expr ) => { - $kdl_node.children().ok_or(ConfigError::KdlParsingError($error.into()))? + $kdl_node.children().ok_or(ConfigError::new_kdl_error($error.into(), $kdl_node.span().offset(), $kdl_node.span().len()))? } } @@ -164,7 +191,7 @@ macro_rules! kdl_children { #[macro_export] macro_rules! kdl_string_arguments { ( $kdl_node:expr ) => {{ - let res: Result, _> = $kdl_node.entries().iter().map(|e| e.value().as_string().ok_or(ConfigError::KdlParsingError("Not a string".into()))).collect(); + let res: Result, _> = $kdl_node.entries().iter().map(|e| e.value().as_string().ok_or(ConfigError::new_kdl_error("Not a string".into(), e.span().offset(), e.span().len()))).collect(); res? }} } @@ -179,7 +206,8 @@ macro_rules! kdl_property_names { #[macro_export] macro_rules! kdl_argument_values { ( $kdl_node:expr ) => { - $kdl_node.entries().iter().map(|arg| arg.value()).collect() + $kdl_node.entries().iter().collect() + // $kdl_node.entries().iter().map(|arg| arg.value()).collect() } } @@ -202,7 +230,7 @@ macro_rules! keys_from_kdl { ( $kdl_node:expr ) => { kdl_string_arguments!($kdl_node) .iter() - .map(|k| Key::from_str(k)) + .map(|k| Key::from_str(k).map_err(|_| ConfigError::new_kdl_error(format!("Invalid key: '{}'", k), $kdl_node.span().offset(), $kdl_node.span().len()))) .collect::>()? } } @@ -218,13 +246,14 @@ macro_rules! actions_from_kdl { } -pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, String> { +pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, ConfigError> { +// pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, ConfigError> { let mut args: Vec = vec![]; - for kdl_value in arguments { - match kdl_value.as_string() { + for kdl_entry in arguments { + match kdl_entry.value().as_string() { Some(string_value) => args.push(string_value.to_string()), None => { - return Err(format!("Failed to parse kdl arguments")); + return Err(ConfigError::new_kdl_error(format!("Argument must be a string"), kdl_entry.span().offset(), kdl_entry.span().len())); } } } @@ -239,7 +268,7 @@ pub fn kdl_child_string_value_for_entry <'a>(command_metadata: &'a KdlDocument, } impl Action { - pub fn new_from_bytes(action_name: &str, bytes: Vec) -> Result> { + pub fn new_from_bytes(action_name: &str, bytes: Vec, action_node: &KdlNode) -> Result { match action_name { "Write" => { Ok(Action::Write(bytes)) @@ -256,39 +285,47 @@ impl Action { "GoToTab" => { let tab_index = *bytes .get(0) - .ok_or_else(|| format!("Cannot create action: {} from bytes: {:?}", action_name, bytes))? + .ok_or_else(|| ConfigError::new_kdl_error(format!("Missing tab index"), action_node.span().offset(), action_node.span().len()))? as u32; Ok(Action::GoToTab(tab_index)) } - _ => Err(format!("Cannot create action: {} from bytes: {:?}", action_name, bytes).into()), + _ => Err(ConfigError::new_kdl_error("Failed to parse action".into(), action_node.span().offset(), action_node.span().len())) } } - pub fn new_from_string(action_name: &str, string: String) -> Result> { + pub fn new_from_string(action_name: &str, string: String, action_node: &KdlNode) -> Result { match action_name { "WriteChars" => Ok(Action::WriteChars(string)), "SwitchToMode" => { match InputMode::from_str(string.as_str()) { Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)), - Err(_e) => return Err(format!("Failed to parse SwitchToMode. Unknown InputMode: {}", string).into()), + Err(_e) => return Err(ConfigError::new_kdl_error(format!("Unknown InputMode '{}'", string), action_node.span().offset(), action_node.span().len())) } }, "Resize" => { - let direction = ResizeDirection::from_str(string.as_str())?; + let direction = ResizeDirection::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::Resize(direction)) } "MoveFocus" => { - let direction = Direction::from_str(string.as_str())?; + let direction = Direction::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::MoveFocus(direction)) } "MoveFocusOrTab" => { - let direction = Direction::from_str(string.as_str())?; + let direction = Direction::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::MoveFocusOrTab(direction)) } "MovePane" => { if string.is_empty() { return Ok(Action::MovePane(None)); } else { - let direction = Direction::from_str(string.as_str())?; + let direction = Direction::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::MovePane(Some(direction))) } } @@ -299,28 +336,34 @@ impl Action { if string.is_empty() { return Ok(Action::NewPane(None)); } else { - let direction = Direction::from_str(string.as_str())?; + let direction = Direction::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::NewPane(Some(direction))) } } "SearchToggleOption" => { - let toggle_option = SearchOption::from_str(string.as_str())?; + let toggle_option = SearchOption::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::SearchToggleOption(toggle_option)) } "Search" => { - let search_direction = SearchDirection::from_str(string.as_str())?; + let search_direction = SearchDirection::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + })?; Ok(Action::Search(search_direction)) } - _ => Err(format!("Cannot create action: '{}' from string: '{:?}'", action_name, string).into()), + _ => Err(ConfigError::new_kdl_error(format!("Unsupported action: {}", action_name), action_node.span().offset(), action_node.span().len())) } } } impl TryFrom<(&str, &KdlDocument)> for PaletteColor { - type Error = Box; + type Error = ConfigError; fn try_from((color_name, theme_colors): (&str, &KdlDocument)) -> Result { - let color = theme_colors.get(color_name).ok_or(format!("Failed to parse color"))?; + let color = theme_colors.get(color_name).ok_or(ConfigError::new_kdl_error(format!("Missing theme color: {}", color_name), theme_colors.span().offset(), theme_colors.span().len()))?; let entry_count = entry_count!(color); let is_rgb = || entry_count == 3; let is_three_digit_hex = || { @@ -342,93 +385,95 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor { }; if is_rgb() { let mut channels = kdl_entries_as_i64!(color); - let r = channels.next().unwrap().ok_or(format!("invalid color"))? as u8; - let g = channels.next().unwrap().ok_or(format!("invalid_color"))? as u8; - let b = channels.next().unwrap().ok_or(format!("invalid_color"))? as u8; + let r = channels.next().unwrap().ok_or(ConfigError::new_kdl_error(format!("invalid rgb color"), color.span().offset(), color.span().len()))? as u8; + let g = channels.next().unwrap().ok_or(ConfigError::new_kdl_error(format!("invalid rgb color"), color.span().offset(), color.span().len()))? as u8; + let b = channels.next().unwrap().ok_or(ConfigError::new_kdl_error(format!("invalid rgb color"), color.span().offset(), color.span().len()))? as u8; Ok(PaletteColor::Rgb((r, g, b))) } else if is_three_digit_hex() { // eg. #fff (hex, will be converted to rgb) let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); s.remove(0); - let r = u8::from_str_radix(&s[0..1], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; - let g = u8::from_str_radix(&s[1..2], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; - let b = u8::from_str_radix(&s[2..3], 16).map_err(|e| format!("Failed to parse color: {}", e))? * 0x11; + let r = u8::from_str_radix(&s[0..1], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))? * 0x11; + let g = u8::from_str_radix(&s[1..2], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))? * 0x11; + let b = u8::from_str_radix(&s[2..3], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))? * 0x11; Ok(PaletteColor::Rgb((r, g, b))) } else if is_six_digit_hex() { // eg. #ffffff (hex, will be converted to rgb) let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); s.remove(0); - let r = u8::from_str_radix(&s[0..2], 16).map_err(|e| format!("Failed to parse color: {}", e))?; - let g = u8::from_str_radix(&s[2..4], 16).map_err(|e| format!("Failed to parse color: {}", e))?; - let b = u8::from_str_radix(&s[4..6], 16).map_err(|e| format!("Failed to parse color: {}", e))?; + let r = u8::from_str_radix(&s[0..2], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))?; + let g = u8::from_str_radix(&s[2..4], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))?; + let b = u8::from_str_radix(&s[4..6], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))?; Ok(PaletteColor::Rgb((r, g, b))) } else if is_eight_bit() { - let n = kdl_first_entry_as_i64!(color).ok_or(format!("Failed to parse color"))?; + let n = kdl_first_entry_as_i64!(color).ok_or(ConfigError::new_kdl_error("Failed to parse color".into(), color.span().offset(), color.span().len()))?; Ok(PaletteColor::EightBit(n as u8)) } else { - Err("Failed to parse color".into()) + Err(ConfigError::new_kdl_error("Failed to parse color".into(), color.span().offset(), color.span().len())) } } } impl TryFrom<&KdlNode> for Action { - type Error = Box; + // type Error = Box; + type Error = ConfigError; fn try_from(kdl_action: &KdlNode) -> Result { let action_name = kdl_name!(kdl_action); - let action_arguments: Vec<&KdlValue> = kdl_argument_values!(kdl_action); + let action_arguments: Vec<&KdlEntry> = kdl_argument_values!(kdl_action); + // let action_arguments: Vec<&KdlValue> = kdl_argument_values!(kdl_action); let action_children: Vec<&KdlDocument> = kdl_children!(kdl_action); match action_name { - "Quit" => parse_kdl_action_arguments!(action_name, action_arguments), - "FocusNextPane" => parse_kdl_action_arguments!(action_name, action_arguments), - "FocusPreviousPane" => parse_kdl_action_arguments!(action_name, action_arguments), - "SwitchFocus" => parse_kdl_action_arguments!(action_name, action_arguments), - "EditScrollback" => parse_kdl_action_arguments!(action_name, action_arguments), - "ScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments), - "ScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments), - "ScrollToBottom" => parse_kdl_action_arguments!(action_name, action_arguments), - "PageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments), - "PageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments), - "HalfPageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments), - "HalfPageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments), - "ToggleFocusFullscreen" => parse_kdl_action_arguments!(action_name, action_arguments), - "TogglePaneFrames" => parse_kdl_action_arguments!(action_name, action_arguments), - "ToggleActiveSyncTab" => parse_kdl_action_arguments!(action_name, action_arguments), - "TogglePaneEmbedOrFloating" => parse_kdl_action_arguments!(action_name, action_arguments), - "ToggleFloatingPanes" => parse_kdl_action_arguments!(action_name, action_arguments), - "CloseFocus" => parse_kdl_action_arguments!(action_name, action_arguments), - "UndoRenamePane" => parse_kdl_action_arguments!(action_name, action_arguments), - "NoOp" => parse_kdl_action_arguments!(action_name, action_arguments), - "GoToNextTab" => parse_kdl_action_arguments!(action_name, action_arguments), - "GoToPreviousTab" => parse_kdl_action_arguments!(action_name, action_arguments), - "CloseTab" => parse_kdl_action_arguments!(action_name, action_arguments), - "ToggleTab" => parse_kdl_action_arguments!(action_name, action_arguments), - "UndoRenameTab" => parse_kdl_action_arguments!(action_name, action_arguments), - "Detach" => parse_kdl_action_arguments!(action_name, action_arguments), - "Copy" => parse_kdl_action_arguments!(action_name, action_arguments), - "Confirm" => parse_kdl_action_arguments!(action_name, action_arguments), - "Deny" => parse_kdl_action_arguments!(action_name, action_arguments), - "Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments), - "WriteChars" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "SwitchToMode" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "Search" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "Resize" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "MoveFocus" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "MoveFocusOrTab" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "MovePane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "DumpScreen" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "NewPane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), - "PaneNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), + "Quit" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "FocusNextPane" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "FocusPreviousPane" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "SwitchFocus" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "EditScrollback" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ScrollToBottom" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "PageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "PageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "HalfPageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "HalfPageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ToggleFocusFullscreen" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "TogglePaneFrames" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ToggleActiveSyncTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "TogglePaneEmbedOrFloating" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ToggleFloatingPanes" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "CloseFocus" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "UndoRenamePane" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "NoOp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "GoToNextTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "GoToPreviousTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "CloseTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ToggleTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "UndoRenameTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "Detach" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "Copy" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "Confirm" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "Deny" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), + "WriteChars" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "SwitchToMode" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "Search" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "Resize" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "MoveFocus" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "MoveFocusOrTab" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "MovePane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "DumpScreen" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "NewPane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "PaneNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), "NewTab" => Ok(Action::NewTab(None, None)), - "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments), - "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), - "SearchInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments), - "SearchToggleOption" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments), + "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), + "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), + "SearchInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), + "SearchToggleOption" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), "Run" => { let arguments = action_arguments.iter().copied(); let mut args = kdl_arguments_that_are_strings(arguments)?; if args.is_empty() { - return Err("No command found in Run action".into()); + return Err(ConfigError::new_kdl_error("No command found in Run action".into(), kdl_action.span().offset(), kdl_action.span().len())); } let command = args.remove(0); let command_metadata = action_children.iter().next(); @@ -444,118 +489,94 @@ impl TryFrom<&KdlNode> for Action { cwd, direction, }; - log::info!("run_command_action: {:?}", run_command_action); Ok(Action::Run(run_command_action)) } _ => { - Err(format!("Failed to parse action: {}", action_name).into()) + Err(ConfigError::new_kdl_error(format!("Unsupported action: {}", action_name).into(), kdl_action.span().offset(), kdl_action.span().len())) } } } } -impl TryFrom<&KdlValue> for Key { - type Error = String; - fn try_from(kdl_value: &KdlValue) -> Result { - let key_str = kdl_value.as_string(); - if key_str.is_none() { - return Err(format!("Failed to parse key: {}", kdl_value)); - } - let key_str = key_str.unwrap(); - let mut modifier: Option<&str> = None; - let mut main_key: Option<&str> = None; - for (index, part) in key_str.split_ascii_whitespace().enumerate() { - // TODO: handle F(u8) - if index == 0 && (part == "Ctrl" || part == "Alt") { - modifier = Some(part); - } else if main_key.is_none() { - main_key = Some(part) - } - } - match (modifier, main_key) { - (Some("Ctrl"), Some(main_key)) => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Ctrl(key_char)) - } else { - Err(format!("Failed to parse key: {}", key_str)) - } - }, - (Some("Alt"), Some(main_key)) => { - match main_key { - // why crate::data::Direction and not just Direction? - // Because it's a different type that we export in this wasm mandated soup - we - // don't like it either! This will be solved as we chip away at our tech-debt - "Left" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Left))), - "Right" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Right))), - "Up" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Up))), - "Down" => Ok(Key::Alt(CharOrArrow::Direction(crate::data::Direction::Down))), - _ => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Alt(CharOrArrow::Char(key_char))) - } else { - Err(format!("Failed to parse key: {}", key_str)) +#[macro_export] +macro_rules! kdl_property_first_arg_as_string { + ( $kdl_node:expr, $property_name:expr ) => { + $kdl_node.get($property_name) + .and_then(|p| p.entries().iter().next()) + .and_then(|p| p.value().as_string()) + } +} + +#[macro_export] +macro_rules! kdl_property_first_arg_as_string_or_error { + ( $kdl_node:expr, $property_name:expr ) => {{ + match $kdl_node.get($property_name) { + Some(property) => { + match property.entries().iter().next() { + Some(first_entry) => { + match first_entry.value().as_string() { + Some(string_entry) => Some((string_entry, first_entry)), + None => { + return Err(ConfigError::new_kdl_error(format!("Property {} must be a string, found: {}", $property_name, first_entry.value()), property.span().offset(), property.span().len())); + } } + }, + None => { + return Err(ConfigError::new_kdl_error(format!("Property {} must have a value", $property_name), property.span().offset(), property.span().len())); } } }, - (None, Some(main_key)) => { - match main_key { - "Backspace" => Ok(Key::Backspace), - "Left" => Ok(Key::Left), - "Right" => Ok(Key::Right), - "Up" => Ok(Key::Up), - "Down" => Ok(Key::Down), - "Home" => Ok(Key::Home), - "End" => Ok(Key::End), - "PageUp" => Ok(Key::PageUp), - "PageDown" => Ok(Key::PageDown), - "Tab" => Ok(Key::BackTab), - "Delete" => Ok(Key::Delete), - "Insert" => Ok(Key::Insert), - "Space" => Ok(Key::Char(' ')), - "Enter" => Ok(Key::Char('\n')), - "Esc" => Ok(Key::Esc), - _ => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Char(key_char)) - } else if key_count > 1 { - if let Some(first_char) = key_chars.next() { - if first_char == 'F' { - let f_index: String = key_chars.collect(); - let f_index: u8 = f_index.parse().map_err(|e| format!("Failed to parse F index: {}", e))?; - if f_index >= 1 && f_index <= 12 { - return Ok(Key::F(f_index)); - } - } + None => None + } + }} +} + +#[macro_export] +macro_rules! kdl_property_first_arg_as_bool_or_error { + ( $kdl_node:expr, $property_name:expr ) => {{ + match $kdl_node.get($property_name) { + Some(property) => { + match property.entries().iter().next() { + Some(first_entry) => { + match first_entry.value().as_bool() { + Some(bool_entry) => Some((bool_entry, first_entry)), + None => { + return Err(ConfigError::new_kdl_error(format!("Property {} must be true or false, found {}", $property_name, first_entry.value()), property.span().offset(), property.span().len())); } - Err(format!("Failed to parse key: {}", key_str)) - } else { - Err(format!("Failed to parse key: {}", key_str)) } + }, + None => { + return Err(ConfigError::new_kdl_error(format!("Property {} must have a value", $property_name), property.span().offset(), property.span().len())); } } - } - _ => Err(format!("Failed to parse key: {}", key_str)) + }, + None => None } - } + }} } #[macro_export] -macro_rules! kdl_property_first_arg_as_string { - ( $kdl_node:expr, $property_name:expr ) => { - $kdl_node.get($property_name) - .and_then(|p| p.entries().iter().next()) - .and_then(|p| p.value().as_string()) - } +macro_rules! kdl_property_first_arg_as_i64_or_error { + ( $kdl_node:expr, $property_name:expr ) => {{ + match $kdl_node.get($property_name) { + Some(property) => { + match property.entries().iter().next() { + Some(first_entry) => { + match first_entry.value().as_i64() { + Some(int_entry) => Some((int_entry, first_entry)), + None => { + return Err(ConfigError::new_kdl_error(format!("Property {} must be numeric, found {}", $property_name, first_entry.value()), property.span().offset(), property.span().len())); + } + } + }, + None => { + return Err(ConfigError::new_kdl_error(format!("Property {} must have a value", $property_name), property.span().offset(), property.span().len())); + } + } + }, + None => None + } + }} } #[macro_export] @@ -644,6 +665,52 @@ macro_rules! kdl_get_bool_property_or_child_value { } } +#[macro_export] +macro_rules! kdl_get_bool_property_or_child_value_with_error { + ( $kdl_node:expr, $name:expr ) => { + match $kdl_node.get($name) { + Some(e) => { + match e.value().as_bool() { + Some(bool_value) => Some(bool_value), + None => { + return Err(kdl_parsing_error!( + format!("{} should be either true or false, found {}", $name, e.value()), + e + )) + } + } + }, + None => { + let child_value = $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)); + match child_value { + Some(e) => { + match e.value().as_bool() { + Some(bool_value) => Some(bool_value), + None => { + return Err(kdl_parsing_error!( + format!("{} should be either true or false, found {}", $name, e.value()), + e + )) + } + } + }, + None => { + if let Some(child_node) = kdl_child_with_name!($kdl_node, $name) { + return Err(kdl_parsing_error!( + format!("{} must have a value, eg. '{} true'", child_node.name().value(), child_node.name().value()), + child_node + )) + } + None + } + } + } + } + } +} + #[macro_export] macro_rules! kdl_get_string_property_or_child_value { ( $kdl_node:expr, $name:expr ) => { @@ -657,6 +724,87 @@ macro_rules! kdl_get_string_property_or_child_value { } } +#[macro_export] +macro_rules! kdl_property_or_child_value_node { + ( $kdl_node:expr, $name:expr ) => { + $kdl_node.get($name) + .or_else(|| $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + ) + } +} + +#[macro_export] +macro_rules! kdl_child_with_name { + ( $kdl_node:expr, $name:expr ) => {{ + $kdl_node.children() + .and_then(|children| children.nodes() + .iter() + .find(|c| c.name().value() == $name) + ) + + }} +} + +#[macro_export] +macro_rules! kdl_get_string_property_or_child_value_with_error { + ( $kdl_node:expr, $name:expr ) => { + match $kdl_node.get($name) { + Some(e) => { + match e.value().as_string() { + Some(string_value) => Some(string_value), + None => { + return Err(kdl_parsing_error!( + format!("{} should be a string, found {} - not a string", $name, e.value()), + e + )) + } + } + }, + None => { + let child_value = $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)); + match child_value { + Some(e) => { + match e.value().as_string() { + Some(string_value) => Some(string_value), + None => { + return Err(kdl_parsing_error!( + format!("{} should be a string, found {} - not a string", $name, e.value()), + e + )) + } + } + }, + None => { + if let Some(child_node) = kdl_child_with_name!($kdl_node, $name) { + return Err(kdl_parsing_error!( + format!("{} must have a value, eg. '{} \"foo\"'", child_node.name().value(), child_node.name().value()), + child_node + )) + } + None + } + } + } + } + } +} + +#[macro_export] +macro_rules! kdl_get_property_or_child { + ( $kdl_node:expr, $name:expr ) => { + $kdl_node.get($name) + // .and_then(|e| e.value().as_string()) + .or_else(|| $kdl_node.children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + ) + } +} + #[macro_export] macro_rules! kdl_get_int_property_or_child_value { ( $kdl_node:expr, $name:expr ) => { @@ -688,37 +836,50 @@ macro_rules! kdl_get_int_entry { impl Options { - pub fn from_kdl(kdl_options: &KdlDocument) -> Self { - let on_force_close = kdl_property_first_arg_as_string!(kdl_options, "on_force_close") - .and_then(|arg| OnForceClose::from_str(arg).ok()); - let simplified_ui = kdl_property_first_arg_as_bool!(kdl_options, "simplified_ui"); - let default_shell = kdl_property_first_arg_as_string!(kdl_options, "default_shell") - .map(|default_shell| PathBuf::from(default_shell)); - let pane_frames = kdl_property_first_arg_as_bool!(kdl_options, "pane_frames"); - let theme = kdl_property_first_arg_as_string!(kdl_options, "theme") - .map(|theme| theme.to_string()); - let default_mode = kdl_property_first_arg_as_string!(kdl_options, "default_mode") - .and_then(|default_mode| InputMode::from_str(default_mode).ok()); - let default_layout = kdl_property_first_arg_as_string!(kdl_options, "default_layout") - .map(|default_layout| PathBuf::from(default_layout)); - let layout_dir = kdl_property_first_arg_as_string!(kdl_options, "layout_dir") - .map(|layout_dir| PathBuf::from(layout_dir)); - let theme_dir = kdl_property_first_arg_as_string!(kdl_options, "theme_dir") - .map(|theme_dir| PathBuf::from(theme_dir)); - let mouse_mode = kdl_property_first_arg_as_bool!(kdl_options, "mouse_mode"); - let scroll_buffer_size = kdl_property_first_arg_as_i64!(kdl_options, "scroll_buffer_size") - .map(|scroll_buffer_size| scroll_buffer_size as usize); - let copy_command = kdl_property_first_arg_as_string!(kdl_options, "copy_command") - .map(|copy_command| copy_command.to_string()); - let copy_clipboard = kdl_property_first_arg_as_string!(kdl_options, "copy_clipboard") - .and_then(|on_force_close| Clipboard::from_str(on_force_close).ok()); - let copy_on_select = kdl_property_first_arg_as_bool!(kdl_options, "copy_on_select"); - let scrollback_editor = kdl_property_first_arg_as_string!(kdl_options, "scrollback_editor") - .map(|scrollback_editor| PathBuf::from(scrollback_editor)); - let mirror_session = kdl_property_first_arg_as_bool!(kdl_options, "mirror_session"); - let session_name = kdl_property_first_arg_as_string!(kdl_options, "session_name").map(|s| s.into()); - let attach_to_session = kdl_property_first_arg_as_bool!(kdl_options, "attach_to_session"); - Options { + pub fn from_kdl(kdl_options: &KdlDocument) -> Result { + let on_force_close = match kdl_property_first_arg_as_string_or_error!(kdl_options, "on_force_close") { + Some((string, entry)) => Some(OnForceClose::from_str(string).map_err(|_| { + kdl_parsing_error!(format!("Invalid value for on_force_close: '{}'", string), entry) + })?), + None => None + }; + let simplified_ui = kdl_property_first_arg_as_bool_or_error!(kdl_options, "simplified_ui").map(|(v, _)| v); + let default_shell = kdl_property_first_arg_as_string_or_error!(kdl_options, "default_shell") + .map(|(string, _entry)| PathBuf::from(string)); + let pane_frames = kdl_property_first_arg_as_bool_or_error!(kdl_options, "pane_frames").map(|(v, _)| v); + let theme = kdl_property_first_arg_as_string_or_error!(kdl_options, "theme") + .map(|(theme, _entry)| theme.to_string()); + let default_mode = match kdl_property_first_arg_as_string_or_error!(kdl_options, "default_mode") { + Some((string, entry)) => Some(InputMode::from_str(string).map_err(|_| { + kdl_parsing_error!(format!("Invalid input mode: '{}'", string), entry) + })?), + None => None + }; + let default_layout = kdl_property_first_arg_as_string_or_error!(kdl_options, "default_layout") + .map(|(string, _entry)| PathBuf::from(string)); + let layout_dir = kdl_property_first_arg_as_string_or_error!(kdl_options, "layout_dir") + .map(|(string, _entry)| PathBuf::from(string)); + let theme_dir = kdl_property_first_arg_as_string_or_error!(kdl_options, "theme_dir") + .map(|(string, _entry)| PathBuf::from(string)); + let mouse_mode = kdl_property_first_arg_as_bool_or_error!(kdl_options, "mouse_mode").map(|(v, _)| v); + let scroll_buffer_size = kdl_property_first_arg_as_i64_or_error!(kdl_options, "scroll_buffer_size") + .map(|(scroll_buffer_size, _entry)| scroll_buffer_size as usize); + let copy_command = kdl_property_first_arg_as_string_or_error!(kdl_options, "copy_command") + .map(|(copy_command, _entry)| copy_command.to_string()); + let copy_clipboard = match kdl_property_first_arg_as_string_or_error!(kdl_options, "copy_clipboard") { + Some((string, entry)) => Some(Clipboard::from_str(string).map_err(|_| { + kdl_parsing_error!(format!("Invalid value for copy_clipboard: '{}'", string), entry) + })?), + None => None + }; + let copy_on_select = kdl_property_first_arg_as_bool_or_error!(kdl_options, "copy_on_select").map(|(v, _)| v); + let scrollback_editor = kdl_property_first_arg_as_string_or_error!(kdl_options, "scrollback_editor") + .map(|(string, _entry)| PathBuf::from(string)); + let mirror_session = kdl_property_first_arg_as_bool_or_error!(kdl_options, "mirror_session").map(|(v, _)| v); + let session_name = kdl_property_first_arg_as_string_or_error!(kdl_options, "session_name") + .map(|(session_name, _entry)| session_name.to_string()); + let attach_to_session = kdl_property_first_arg_as_bool_or_error!(kdl_options, "attach_to_session").map(|(v, _)| v); + Ok(Options { simplified_ui, theme, default_mode, @@ -737,15 +898,26 @@ impl Options { scrollback_editor, session_name, attach_to_session, - } + }) } } impl RunPlugin { pub fn from_kdl(kdl_node: &KdlNode) -> Result { let _allow_exec_host_cmd = kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false); - let string_url = kdl_get_child_entry_string_value!(kdl_node, "location").ok_or(ConfigError::KdlParsingError("Plugins must have a location".into()))?; - let url = Url::parse(string_url).map_err(|e| ConfigError::KdlParsingError(format!("Failed to aprse url: {:?}", e)))?; + let string_url = kdl_get_child_entry_string_value!(kdl_node, "location") + .ok_or( + ConfigError::new_kdl_error( + "Plugins must have a location".into(), + kdl_node.span().offset(), + kdl_node.span().len() + ) + )?; + let url = Url::parse(string_url).map_err(|e| ConfigError::new_kdl_error( + format!("Failed to parse url: {:?}", e), + kdl_node.span().offset(), + kdl_node.span().len(), + ))?; let location = RunPluginLocation::try_from(url)?; Ok(RunPlugin { _allow_exec_host_cmd, @@ -754,8 +926,28 @@ impl RunPlugin { } } impl Layout { - pub fn from_kdl(kdl_layout: &KdlDocument) -> Result { - KdlLayoutParser::new(&kdl_layout).parse() + pub fn from_kdl(raw_layout: &str, file_name: String) -> Result { + KdlLayoutParser::new(raw_layout, file_name.clone()).parse().map_err(|e| { + match e { + ConfigError::KdlError(kdl_error) => ConfigError::KdlError(kdl_error.add_src(file_name, String::from(raw_layout))), + ConfigError::KdlDeserializationError(kdl_error) => { + let error_message = match kdl_error.kind { + kdl::KdlErrorKind::Context("valid node terminator") => { + format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") + }, + _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), + }; + let kdl_error = KdlError { + error_message, + src: Some(NamedSource::new(file_name, String::from(raw_layout))), + offset: Some(kdl_error.span.offset()), + len: Some(kdl_error.span.len()), + }; + ConfigError::KdlError(kdl_error) + }, + e => e + } + }) } } impl EnvironmentVariables { @@ -767,7 +959,7 @@ impl EnvironmentVariables { let env_var_int_value = kdl_first_entry_as_i64!(env_var).map(|s| format!("{}", s.to_string())); let env_var_value = env_var_str_value .or(env_var_int_value) - .ok_or::>(format!("Failed to parse env var: {:?}", env_var_name).into())?; + .ok_or(ConfigError::new_kdl_error(format!("Failed to parse env var: {:?}", env_var_name), env_var.span().offset(), env_var.span().len()))?; env.insert(env_var_name.into(), env_var_value); } Ok(EnvironmentVariables::from_data(env)) @@ -788,7 +980,11 @@ impl Keybinds { } for key_block in all_nodes { if kdl_name!(key_block) != "bind" && kdl_name!(key_block) != "unbind" { - return Err(ConfigError::KdlParsingError(format!("Unknown keybind instruction: '{}'", kdl_name!(key_block)))); + return Err(ConfigError::new_kdl_error( + format!("Unknown keybind instruction: '{}'", kdl_name!(key_block)), + key_block.span().offset(), + key_block.span().len(), + )); } } Ok(()) @@ -800,7 +996,7 @@ impl Keybinds { if kdl_name!(block) == "shared_except" || kdl_name!(block) == "shared" { let mut modes_to_exclude = vec![]; for mode_name in kdl_string_arguments!(block) { - modes_to_exclude.push(InputMode::from_str(mode_name)?); + modes_to_exclude.push(InputMode::from_str(mode_name).map_err(|_| ConfigError::new_kdl_error(format!("Invalid mode: '{}'", mode_name), block.name().span().offset(), block.name().span().len()))?); } for mode in InputMode::iter() { if modes_to_exclude.contains(&mode) { @@ -862,7 +1058,7 @@ impl Keybinds { } fn input_mode_keybindings <'a>(mode: &KdlNode, keybinds_from_config: &'a mut Keybinds) -> Result<&'a mut HashMap>, ConfigError> { let mode_name = kdl_name!(mode); - let input_mode = InputMode::from_str(mode_name)?; + let input_mode = InputMode::from_str(mode_name).map_err(|_| ConfigError::new_kdl_error(format!("Invalid mode: '{}'", mode_name), mode.name().span().offset(), mode.name().span().len()))?; let input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&input_mode); let clear_defaults_for_mode = kdl_arg_is_truthy!(mode, "clear-defaults"); if clear_defaults_for_mode { @@ -874,7 +1070,9 @@ impl Keybinds { impl RunCommand { pub fn from_kdl(kdl_node: &KdlNode) -> Result { - let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or(ConfigError::KdlParsingError("Command must have a cmd value".into()))?); + let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd") + .ok_or(ConfigError::new_kdl_error("Command must have a cmd value".into(), kdl_node.span().offset(), kdl_node.span().len()))? + ); let cwd = kdl_get_child_entry_string_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); let args = match kdl_get_child!(kdl_node, "args") { Some(kdl_args) => { @@ -899,7 +1097,7 @@ impl Config { if let Some(kdl_keybinds) = kdl_config.get("keybinds") { config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds)?; } - let config_options = Options::from_kdl(&kdl_config); + let config_options = Options::from_kdl(&kdl_config)?; config.options = config.options.merge(config_options); if let Some(kdl_themes) = kdl_config.get("themes") { let config_themes = Themes::from_kdl(kdl_themes)?; @@ -929,7 +1127,7 @@ impl PluginsConfig { let plugin_tag = PluginTag::new(plugin_name); let path = kdl_children_property_first_arg_as_string!(plugin_config, "path") .map(|path| PathBuf::from(path)) - .ok_or::>("Plugin path not found".into())?; + .ok_or(ConfigError::new_kdl_error("Plugin path not found or invalid".into(), plugin_config.span().offset(), plugin_config.span().len()))?; let allow_exec_host_cmd = kdl_children_property_first_arg_as_bool!(plugin_config, "_allow_exec_host_cmd") .unwrap_or(false); let plugin_config = PluginConfig { @@ -991,7 +1189,7 @@ impl Theme { let mut kdl_config = String::new(); file.read_to_string(&mut kdl_config)?; let kdl_config: KdlDocument = kdl_config.parse()?; - let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::KdlParsingError("No theme found in file".into()))?; + let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::new_kdl_error("No theme found in file".into(), kdl_config.span().offset(), kdl_config.span().len()))?; let theme_name = kdl_name!(kdl_config); let theme_colors = kdl_children_or_error!(kdl_config, "empty theme"); Ok((theme_name.into(), Theme { diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index cf38718c23..ef22a37873 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -454,11 +454,11 @@ impl Setup { let chosen_layout = cli_args .layout .clone() - .or_else(|| cli_config_options.as_ref().and_then(|cli_options| cli_options.default_layout.clone())); + .or_else(|| cli_config_options.as_ref().and_then(|cli_options| cli_options.default_layout.clone())) + .or_else(|| config.options.default_layout.clone()); // we merge-override the config here because the layout might contain configuration // that needs to take precedence - let (layout, config) = Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir.clone(), config)?; - Ok((layout, config)) + Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir.clone(), config) } fn handle_setup_commands(cli_args: &CliArgs) { if let Some(Command::Setup(ref setup)) = &cli_args.command { From 1b839119d2e5e55c37d9802e8be0295dc07ab1a1 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 29 Sep 2022 09:44:28 +0200 Subject: [PATCH 39/55] feat(client): convert old YAML files on startup --- src/commands.rs | 4 +- .../convert_old_yaml_files.rs | 253 ++++++++++++++++++ zellij-client/src/old_config_converter/mod.rs | 2 + zellij-utils/src/input/config.rs | 1 - 4 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 zellij-client/src/old_config_converter/convert_old_yaml_files.rs diff --git a/src/commands.rs b/src/commands.rs index fd0cbd2bab..6d5b75643c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -12,7 +12,7 @@ use std::process; use zellij_utils::input::actions::Action; use zellij_utils::input::config::ConfigError; use zellij_client::start_client as start_client_impl; -use zellij_client::old_config_converter::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl}; +use zellij_client::old_config_converter::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl, convert_old_yaml_files}; use zellij_client::{os_input_output::get_client_os_input, ClientInfo}; use zellij_server::os_input_output::get_server_os_input; use zellij_server::start_server as start_server_impl; @@ -313,6 +313,8 @@ fn attach_with_session_name( } pub(crate) fn start_client(opts: CliArgs) { + // look for old YAML config/layout/theme files and convert them to KDL + convert_old_yaml_files(&opts); let (config, layout, config_options) = match Setup::from_cli_args(&opts) { Ok(results) => results, Err(e) => { diff --git a/zellij-client/src/old_config_converter/convert_old_yaml_files.rs b/zellij-client/src/old_config_converter/convert_old_yaml_files.rs new file mode 100644 index 0000000000..a68bb795bf --- /dev/null +++ b/zellij-client/src/old_config_converter/convert_old_yaml_files.rs @@ -0,0 +1,253 @@ +use zellij_utils::{ + cli::CliArgs, + setup::{find_default_config_dir, get_layout_dir, get_theme_dir}, +}; +use std::path::PathBuf; +use super::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl}; + +const OLD_CONFIG_NAME: &str = "config.yaml"; + +pub fn convert_old_yaml_files(opts: &CliArgs) { + let config_dir = opts.config_dir.clone().or_else(find_default_config_dir); + let layout_dir = get_layout_dir(config_dir.clone()); + let theme_dir = get_theme_dir(find_default_config_dir()); + let specified_config_location = opts.config.as_ref(); + + let mut layout_files_to_convert = vec![]; + let mut theme_files_to_convert = vec![]; + if let Some(layout) = opts.layout.as_ref() { + if layout.extension().map(|s| s.to_string_lossy().to_string()) == Some("yaml".into()) { + if layout.exists() { + layout_files_to_convert.push((layout.clone(), true)); + } + } + } + layout_files_to_convert.dedup(); + if let Some(layout_dir) = layout_dir { + if let Ok(files) = std::fs::read_dir(layout_dir) { + for file in files { + if let Ok(file) = file { + if file.path().extension().map(|s| s.to_string_lossy().to_string()) == Some("yaml".into()) { + let mut new_file_path = file.path().clone(); + new_file_path.set_extension("kdl"); + if !new_file_path.exists() { + layout_files_to_convert.push((file.path().clone(), false)); + } + } + } + } + } + } + + if let Some(theme_dir) = theme_dir { + if theme_dir.is_dir() { + if let Ok(files) = std::fs::read_dir(theme_dir) { + for entry in files.flatten() { + if let Some(extension) = entry.path().extension() { + if extension == "yaml" { + let mut new_file_path = entry.path().clone(); + new_file_path.set_extension("kdl"); + if !new_file_path.exists() { + theme_files_to_convert.push(entry.path()) + } + } + } + } + } + } + } + + + + if let Some(config_dir) = config_dir { + let yaml_config_location = specified_config_location + .cloned() + .filter(|c| c.extension().map(|s| s.to_string_lossy().to_string()) == Some("yaml".into())); + let specified_yaml_config_location = yaml_config_location.is_some(); + let config_location = yaml_config_location.unwrap_or_else(|| config_dir.join(OLD_CONFIG_NAME)); + match convert_yaml(config_location, layout_files_to_convert, theme_files_to_convert, specified_yaml_config_location) { + Ok(should_exit) => { + if should_exit { + std::process::exit(0); + } + } + Err(e) => { + eprintln!(""); + eprintln!("\u{1b}[1;31mFailed to convert yaml config\u{1b}[m: {}", e); + eprintln!(""); + std::process::exit(1); + } + } + } +} + +fn print_conversion_title_message() { + println!(""); + println!("\u{1b}[1mZellij has moved to a new configuration format (KDL - https://kdl.dev) and has now been run with an old YAML configuration/layout/theme file.\u{1b}[m"); +} + +fn print_converting_config_message(old_file_name: String, new_file_name: String) { + println!( + "- Converting configuration file: \u{1b}[1;36m{}\u{1b}[m to the new configuration format at the same location: \u{1b}[1;36m{}\u{1b}[m", + old_file_name, + new_file_name + ); +} + +fn print_conversion_layouts_message(layout_files_to_convert: Vec<(PathBuf, bool)>) { + println!("- Converting the following layout YAML files to KDL files in the same location:"); + for (layout_file, _was_explicitly_set) in layout_files_to_convert.iter() { + let mut new_layout_file_name = layout_file.clone(); + new_layout_file_name.set_extension("kdl"); + println!( + "\u{1b}[1;36m{}\u{1b}[m => \u{1b}[1;36m{}\u{1b}[m", + layout_file.as_path().as_os_str().to_string_lossy(), + new_layout_file_name.as_path().as_os_str().to_string_lossy() + ); + } +} + +fn print_conversion_themes_message(theme_files_to_convert: Vec) { + println!("- Converting the following theme YAML files to KDL files in the same location:"); + for theme_file in theme_files_to_convert.iter() { + let mut new_theme_file_name = theme_file.clone(); + new_theme_file_name.set_extension("kdl"); + println!( + "\u{1b}[1;36m{}\u{1b}[m => \u{1b}[1;36m{}\u{1b}[m", + theme_file.as_path().as_os_str().to_string_lossy(), + new_theme_file_name.as_path().as_os_str().to_string_lossy() + ); + } +} + +fn print_no_actions_and_wait_for_user_input() -> Result<(), String> { + println!("\u{1b}[1;32mNo actions are required of you. Press ENTER to continue.\u{1b}[m"); + std::io::stdin().read_line(&mut String::new()).map_err(|e| format!("Failed to read from STDIN: {:?}", e))?; + Ok(()) +} + +fn print_remain_unmodified_message(will_exit: bool) { + println!("The original file(s) will remain unmodified."); + if !will_exit { + println!("Will then use the new converted file(s) for this and the next runs."); + } + println!(""); +} + +fn print_flag_help_message(layout_files_to_convert: Vec<(PathBuf, bool)>, yaml_config_file: &PathBuf, yaml_config_was_explicitly_set: bool) -> Result<(), String> { + println!("\u{1b}[1;32mWhat do you need to do?\u{1b}[m"); + match layout_files_to_convert.iter().find(|(_f, explicit)| *explicit) { + Some((explicitly_specified_layout, _)) => { + let mut kdl_config_file_path = yaml_config_file.clone(); + let mut kdl_explicitly_specified_layout= explicitly_specified_layout.clone(); + kdl_config_file_path.set_extension("kdl"); + kdl_explicitly_specified_layout.set_extension("kdl"); + if yaml_config_was_explicitly_set { + println!("Since both the YAML config and a YAML layout file were explicitly specified, you'll need to re-run Zellij and point it to the new files:"); + println!( + "\u{1b}[1;33mzellij --config {} --layout {}\u{1b}[m", + kdl_config_file_path.as_path().as_os_str().to_string_lossy().to_string(), + kdl_explicitly_specified_layout.as_path().as_os_str().to_string_lossy().to_string(), + ); + } else { + println!("Since a YAML layout was explicitly specified, you'll need to re-run Zellij and point it to the new layout:"); + println!( + "\u{1b}[1;33mzellij --layout {}\u{1b}[m", + kdl_explicitly_specified_layout.as_path().as_os_str().to_string_lossy().to_string(), + ); + } + }, + None => { + if yaml_config_was_explicitly_set { + let mut kdl_config_file_path = yaml_config_file.clone(); + kdl_config_file_path.set_extension("kdl"); + println!("Since the YAML config was explicitly specified, you'll need to re-run Zellij and point it to the new config:"); + println!( + "\u{1b}[1;33mzellij --config {}\u{1b}[m", + kdl_config_file_path.as_path().as_os_str().to_string_lossy().to_string(), + ); + } + } + } + println!(""); + println!("\u{1b}[1;32mPress ENTER to continue.\u{1b}[m"); + std::io::stdin().read_line(&mut String::new()).map_err(|e| format!("Failed to read from STDIN: {:?}", e))?; + Ok(()) +} + +fn convert_layouts(layout_files_to_convert: Vec<(PathBuf, bool)>) -> Result<(), String> { + for (layout_file, _was_explicitly_set) in layout_files_to_convert { + let raw_layout_file = std::fs::read_to_string(&layout_file).map_err(|e| format!("Failed to read layout file {:?}: {:?}", layout_file, e))?; + let kdl_layout = layout_yaml_to_layout_kdl(&raw_layout_file)?; + let mut new_layout_file = layout_file.clone(); + new_layout_file.set_extension("kdl"); + std::fs::write(&new_layout_file, kdl_layout).map_err(|e| format!("Failed to write new layout file to {:?}: {:?}", new_layout_file, e))?; + } + Ok(()) +} + +fn convert_themes(theme_files_to_convert: Vec) -> Result<(), String> { + for theme_file in theme_files_to_convert { + let raw_theme_file = std::fs::read_to_string(&theme_file).map_err(|e| format!("Failed to read theme file {:?}: {:?}", theme_file, e))?; + let kdl_theme = config_yaml_to_config_kdl(&raw_theme_file, true)?; + let mut new_theme_file = theme_file.clone(); + new_theme_file.set_extension("kdl"); + std::fs::write(&new_theme_file, kdl_theme).map_err(|e| format!("Failed to write new theme file to {:?}: {:?}", new_theme_file, e))?; + } + Ok(()) +} + +fn convert_config(yaml_config_file: PathBuf, new_config_file: PathBuf) -> Result<(), String> { + if yaml_config_file.exists() && !new_config_file.exists() { + let raw_config_file = std::fs::read_to_string(&yaml_config_file).map_err(|e| format!("Failed to read config file {:?}: {:?}", yaml_config_file, e))?; + let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; + std::fs::write(&new_config_file, kdl_config).map_err(|e| format!("Failed to write new config file to {:?}: {:?}", new_config_file, e))?; + } + Ok(()) +} + +fn convert_yaml(yaml_config_file: PathBuf, layout_files_to_convert: Vec<(PathBuf, bool)>, theme_files_to_convert: Vec, yaml_config_was_explicitly_set: bool) -> Result { + let mut should_exit = false; + let mut new_config_file = yaml_config_file.clone(); + new_config_file.set_extension("kdl"); + let yaml_config_file_exists = yaml_config_file.exists(); + let layout_was_explicitly_set = layout_files_to_convert.iter().find(|(_l, was_explicitly_set)| *was_explicitly_set).is_some(); + let new_config_file_exists = new_config_file.exists(); + let no_need_to_convert_config = (new_config_file_exists && !yaml_config_was_explicitly_set) || !yaml_config_file_exists; + if no_need_to_convert_config && layout_files_to_convert.is_empty() && theme_files_to_convert.is_empty() && !layout_was_explicitly_set { + // Nothing to do... + return Ok(should_exit); + } + print_conversion_title_message(); + if yaml_config_file_exists && !new_config_file_exists { + print_converting_config_message( + yaml_config_file.as_path().as_os_str().to_string_lossy().to_string(), + new_config_file.as_path().as_os_str().to_string_lossy().to_string() + ); + } else if yaml_config_file_exists && new_config_file_exists && yaml_config_was_explicitly_set { + return Err( + format!( + "Specified old YAML format config (--config {}) but a new KDL file exists in that location. To fix, point to it the new file instead: zellij --config {}", + yaml_config_file.as_path().as_os_str().to_string_lossy().to_string(), + new_config_file.as_path().as_os_str().to_string_lossy().to_string() + ) + ); + } + if !layout_files_to_convert.is_empty() { + print_conversion_layouts_message(layout_files_to_convert.clone()); + } + if !theme_files_to_convert.is_empty() { + print_conversion_themes_message(theme_files_to_convert.clone()); + } + print_remain_unmodified_message(layout_was_explicitly_set || yaml_config_was_explicitly_set); + if layout_was_explicitly_set || yaml_config_was_explicitly_set { + print_flag_help_message(layout_files_to_convert.clone(), &yaml_config_file, yaml_config_was_explicitly_set)?; + should_exit = true; + } else { + print_no_actions_and_wait_for_user_input()?; + } + convert_layouts(layout_files_to_convert)?; + convert_themes(theme_files_to_convert)?; + convert_config(yaml_config_file, new_config_file)?; + Ok(should_exit) +} diff --git a/zellij-client/src/old_config_converter/mod.rs b/zellij-client/src/old_config_converter/mod.rs index 1999a56f86..d120562d8b 100644 --- a/zellij-client/src/old_config_converter/mod.rs +++ b/zellij-client/src/old_config_converter/mod.rs @@ -1,4 +1,6 @@ mod old_config; mod old_layout; +mod convert_old_yaml_files; pub use old_config::config_yaml_to_config_kdl; pub use old_layout::layout_yaml_to_layout_kdl; +pub use convert_old_yaml_files::convert_old_yaml_files; diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index d339dc7276..b7c7385fbc 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -182,7 +182,6 @@ impl Config { match Config::from_kdl(&kdl_config, default_config) { Ok(config) => Ok(config), Err(ConfigError::KdlDeserializationError(kdl_error)) => { - println!("kdl_error.code: {:?}", kdl_error.kind); let error_message = match kdl_error.kind { kdl::KdlErrorKind::Context("valid node terminator") => { format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") From d4a6971c30e68252acbe8b2a3482ecf141f17ab0 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 30 Sep 2022 14:02:53 +0200 Subject: [PATCH 40/55] fix: various bugs and styling issues --- .../status-bar/src/tip/data/quicknav.rs | 4 +- src/commands.rs | 60 +- src/main.rs | 39 +- zellij-client/src/cli_client.rs | 14 +- zellij-client/src/input_handler.rs | 89 +- zellij-client/src/lib.rs | 19 +- .../convert_old_yaml_files.rs | 149 +- zellij-client/src/old_config_converter/mod.rs | 4 +- .../src/old_config_converter/old_config.rs | 331 +++-- .../src/old_config_converter/old_layout.rs | 193 ++- .../unit/convert_config_tests.rs | 64 +- .../unit/convert_layout_tests.rs | 73 +- zellij-client/src/sessions.rs | 18 - zellij-server/src/lib.rs | 5 +- zellij-server/src/os_input_output.rs | 1 - zellij-server/src/panes/tiled_panes/mod.rs | 12 +- .../src/panes/tiled_panes/pane_resizer.rs | 8 +- .../src/panes/tiled_panes/tiled_pane_grid.rs | 7 +- zellij-server/src/pty.rs | 37 +- zellij-server/src/route.rs | 52 +- zellij-server/src/screen.rs | 379 +++-- .../src/tab/unit/tab_integration_tests.rs | 28 +- zellij-server/src/tab/unit/tab_tests.rs | 14 +- zellij-server/src/unit/screen_tests.rs | 1043 +++++++++---- zellij-utils/src/cli.rs | 20 +- zellij-utils/src/data.rs | 94 +- zellij-utils/src/envs.rs | 6 +- zellij-utils/src/input/actions.rs | 111 +- zellij-utils/src/input/config.rs | 377 +++-- zellij-utils/src/input/keybinds.rs | 17 +- zellij-utils/src/input/layout.rs | 136 +- zellij-utils/src/input/options.rs | 8 +- zellij-utils/src/input/plugins.rs | 2 +- zellij-utils/src/input/theme.rs | 5 +- zellij-utils/src/input/unit/keybinds_test.rs | 315 ++-- zellij-utils/src/input/unit/layout_test.rs | 353 +++-- zellij-utils/src/input/unit/theme_test.rs | 2 +- zellij-utils/src/ipc.rs | 2 +- zellij-utils/src/kdl/kdl_layout_parser.rs | 603 +++++--- zellij-utils/src/kdl/mod.rs | 1307 +++++++++++------ zellij-utils/src/lib.rs | 6 +- zellij-utils/src/pane_size.rs | 9 +- zellij-utils/src/setup.rs | 139 +- 43 files changed, 4037 insertions(+), 2118 deletions(-) delete mode 100644 zellij-client/src/sessions.rs diff --git a/default-plugins/status-bar/src/tip/data/quicknav.rs b/default-plugins/status-bar/src/tip/data/quicknav.rs index ca3fd376bc..57d60a2d93 100644 --- a/default-plugins/status-bar/src/tip/data/quicknav.rs +++ b/default-plugins/status-bar/src/tip/data/quicknav.rs @@ -79,7 +79,9 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups { &[Action::Resize(ResizeDirection::Decrease)], ], ); - if resize_keys.contains(&Key::Alt(CharOrArrow::Char('='))) && resize_keys.contains(&Key::Alt(CharOrArrow::Char('+'))) { + if resize_keys.contains(&Key::Alt(CharOrArrow::Char('='))) + && resize_keys.contains(&Key::Alt(CharOrArrow::Char('+'))) + { resize_keys.retain(|k| k != &Key::Alt(CharOrArrow::Char('='))); } let resize = if resize_keys.is_empty() { diff --git a/src/commands.rs b/src/commands.rs index 6d5b75643c..db6e94d726 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -6,16 +6,18 @@ use crate::sessions::{ session_exists, ActiveSession, SessionNameMatch, }; use dialoguer::Confirm; -use miette::{Result, Report}; +use miette::{Report, Result}; use std::path::PathBuf; use std::process; -use zellij_utils::input::actions::Action; -use zellij_utils::input::config::ConfigError; +use zellij_client::old_config_converter::{ + config_yaml_to_config_kdl, convert_old_yaml_files, layout_yaml_to_layout_kdl, +}; use zellij_client::start_client as start_client_impl; -use zellij_client::old_config_converter::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl, convert_old_yaml_files}; use zellij_client::{os_input_output::get_client_os_input, ClientInfo}; use zellij_server::os_input_output::get_server_os_input; use zellij_server::start_server as start_server_impl; +use zellij_utils::input::actions::Action; +use zellij_utils::input::config::ConfigError; use zellij_utils::input::options::Options; use zellij_utils::nix; use zellij_utils::{ @@ -26,11 +28,6 @@ use zellij_utils::{ use std::{fs::File, io::prelude::*}; -#[cfg(feature = "unstable")] -use miette::IntoDiagnostic; -#[cfg(feature = "unstable")] -use zellij_utils::input::actions::ActionsFromYaml; - pub(crate) use crate::sessions::list_sessions; pub(crate) fn kill_all_sessions(yes: bool) { @@ -123,7 +120,10 @@ fn find_indexed_session( } } -pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, requested_session_name: Option) { +pub(crate) fn send_action_to_session( + cli_action: zellij_utils::cli::CliAction, + requested_session_name: Option, +) { match get_active_session() { ActiveSession::None => { eprintln!("There is no active session!"); @@ -132,7 +132,10 @@ pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, r ActiveSession::One(session_name) => { if let Some(requested_session_name) = requested_session_name { if requested_session_name != session_name { - eprintln!("Session '{}' not found. The following sessions are active:", requested_session_name); + eprintln!( + "Session '{}' not found. The following sessions are active:", + requested_session_name + ); eprintln!("{}", session_name); std::process::exit(1); } @@ -145,7 +148,10 @@ pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, r if existing_sessions.contains(&session_name) { attach_with_cli_client(cli_action, &session_name); } else { - eprintln!("Session '{}' not found. The following sessions are active:", session_name); + eprintln!( + "Session '{}' not found. The following sessions are active:", + session_name + ); print_sessions(existing_sessions); std::process::exit(1); } @@ -159,7 +165,7 @@ pub(crate) fn send_action_to_session(cli_action: zellij_utils::cli::CliAction, r }, }; } -pub(crate) fn convert_old_config_file(old_config_file: PathBuf, output_location: Option) { +pub(crate) fn convert_old_config_file(old_config_file: PathBuf) { match File::open(&old_config_file) { Ok(mut handle) => { let mut raw_config_file = String::new(); @@ -172,17 +178,17 @@ pub(crate) fn convert_old_config_file(old_config_file: PathBuf, output_location: Err(e) => { eprintln!("Failed to convert config: {}", e); process::exit(1); - } + }, } }, Err(e) => { eprintln!("Failed to open file: {}", e); process::exit(1); - } + }, } } -pub(crate) fn convert_old_layout_file(old_layout_file: PathBuf, output_location: Option) { +pub(crate) fn convert_old_layout_file(old_layout_file: PathBuf) { match File::open(&old_layout_file) { Ok(mut handle) => { let mut raw_layout_file = String::new(); @@ -195,17 +201,17 @@ pub(crate) fn convert_old_layout_file(old_layout_file: PathBuf, output_location: Err(e) => { eprintln!("Failed to convert layout: {}", e); process::exit(1); - } + }, } }, Err(e) => { eprintln!("Failed to open file: {}", e); process::exit(1); - } + }, } } -pub(crate) fn convert_old_theme_file(old_theme_file: PathBuf, output_location: Option) { +pub(crate) fn convert_old_theme_file(old_theme_file: PathBuf) { match File::open(&old_theme_file) { Ok(mut handle) => { let mut raw_config_file = String::new(); @@ -218,13 +224,13 @@ pub(crate) fn convert_old_theme_file(old_theme_file: PathBuf, output_location: O Err(e) => { eprintln!("Failed to convert config: {}", e); process::exit(1); - } + }, } }, Err(e) => { eprintln!("Failed to open file: {}", e); process::exit(1); - } + }, } } @@ -232,18 +238,14 @@ fn attach_with_cli_client(cli_action: zellij_utils::cli::CliAction, session_name let os_input = get_os_input(zellij_client::os_input_output::get_client_os_input); match Action::actions_from_cli(cli_action) { Ok(actions) => { - zellij_client::cli_client::start_cli_client( - Box::new(os_input), - session_name, - actions, - ); + zellij_client::cli_client::start_cli_client(Box::new(os_input), session_name, actions); std::process::exit(0); - } + }, Err(e) => { eprintln!("{}", e); log::error!("Error sending action: {}", e); std::process::exit(2); - } + }, } } @@ -420,7 +422,7 @@ pub(crate) fn start_client(opts: CliArgs) { ClientInfo::New(session_name.clone()), Some(layout), ); - } + }, } process::exit(0); // TODO: why is this here? } diff --git a/src/main.rs b/src/main.rs index cc9a9f7dfc..793fdcf511 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod tests; use zellij_utils::{ clap::Parser, - cli::{CliArgs, CliAction, Command, Sessions}, + cli::{CliAction, CliArgs, Command, Sessions}, logging::*, }; @@ -19,7 +19,14 @@ fn main() { commands::send_action_to_session(cli_action, opts.session); std::process::exit(0); } - if let Some(Command::Sessions(Sessions::Command{ command, direction, cwd, args, floating })) = opts.command { + if let Some(Command::Sessions(Sessions::Command { + command, + direction, + cwd, + args, + floating, + })) = opts.command + { let command_cli_action = CliAction::NewPane { command, direction, @@ -30,16 +37,32 @@ fn main() { commands::send_action_to_session(command_cli_action, opts.session); std::process::exit(0); } - if let Some(Command::Sessions(Sessions::ConvertConfig { old_config_file, output })) = opts.command { - commands::convert_old_config_file(old_config_file, output); + if let Some(Command::Sessions(Sessions::Edit { + file, + direction, + line_number, + floating, + })) = opts.command + { + let command_cli_action = CliAction::Edit { + file, + direction, + line_number, + floating, + }; + commands::send_action_to_session(command_cli_action, opts.session); + std::process::exit(0); + } + if let Some(Command::Sessions(Sessions::ConvertConfig { old_config_file })) = opts.command { + commands::convert_old_config_file(old_config_file); std::process::exit(0); } - if let Some(Command::Sessions(Sessions::ConvertLayout { old_layout_file, output })) = opts.command { - commands::convert_old_layout_file(old_layout_file, output); + if let Some(Command::Sessions(Sessions::ConvertLayout { old_layout_file })) = opts.command { + commands::convert_old_layout_file(old_layout_file); std::process::exit(0); } - if let Some(Command::Sessions(Sessions::ConvertTheme { old_theme_file, output })) = opts.command { - commands::convert_old_theme_file(old_theme_file, output); + if let Some(Command::Sessions(Sessions::ConvertTheme { old_theme_file })) = opts.command { + commands::convert_old_theme_file(old_theme_file); std::process::exit(0); } } diff --git a/zellij-client/src/cli_client.rs b/zellij-client/src/cli_client.rs index a6d02195de..95cd9d133d 100644 --- a/zellij-client/src/cli_client.rs +++ b/zellij-client/src/cli_client.rs @@ -1,21 +1,15 @@ //! The `[cli_client]` is used to attach to a running server session //! and dispatch actions, that are specified through the command line. -use std::{fs, path::PathBuf}; use std::process; +use std::{fs, path::PathBuf}; -use crate::{ - os_input_output::ClientOsApi, -}; +use crate::os_input_output::ClientOsApi; use zellij_utils::{ input::actions::Action, ipc::{ClientToServerMsg, ServerToClientMsg}, }; -pub fn start_cli_client( - os_input: Box, - session_name: &str, - actions: Vec, -) { +pub fn start_cli_client(os_input: Box, session_name: &str, actions: Vec) { let zellij_ipc_pipe: PathBuf = { let mut sock_dir = zellij_utils::consts::ZELLIJ_SOCK_DIR.clone(); fs::create_dir_all(&sock_dir).unwrap(); @@ -34,7 +28,7 @@ pub fn start_cli_client( os_input.send_to_server(ClientToServerMsg::ClientExited); process::exit(0); }, - _ => {} + _ => {}, } } } diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs index ecaeeedc1d..076ab35d6f 100644 --- a/zellij-client/src/input_handler.rs +++ b/zellij-client/src/input_handler.rs @@ -132,7 +132,9 @@ impl InputHandler { } fn handle_key(&mut self, key: &Key, raw_bytes: Vec) { let keybinds = &self.config.keybinds; - for action in keybinds.get_actions_for_key_in_mode_or_default_action(&self.mode, key, raw_bytes) { + for action in + keybinds.get_actions_for_key_in_mode_or_default_action(&self.mode, key, raw_bytes) + { let should_exit = self.dispatch_action(action, None); if should_exit { self.should_exit = true; @@ -229,65 +231,6 @@ impl InputHandler { }, } } - fn handle_actions(&mut self, actions: Vec, session_name: &str, clients: Vec) { - let mut detached = false; - for action in actions { - match action { - Action::Quit => { - crate::sessions::kill_session(session_name); - break; - }, - Action::Detach => { - let first = clients.first().unwrap(); - let last = clients.last().unwrap(); - self.os_input - .send_to_server(ClientToServerMsg::DetachSession(vec![*first, *last])); - detached = true; - break; - }, - // Actions, that are independent from the specific client - // and not session idempotent should be specified here - Action::NewTab(..) - | Action::Run(_) - | Action::NewPane(_) - | Action::WriteChars(_) - | Action::EditScrollback - | Action::DumpScreen(_) - | Action::ToggleActiveSyncTab - | Action::ToggleFloatingPanes - | Action::TogglePaneEmbedOrFloating - | Action::TogglePaneFrames - | Action::ToggleFocusFullscreen - | Action::Write(_) => { - let client_id = clients.first().unwrap(); - log::debug!("Sending action to client: {}", client_id); - self.dispatch_action(action, Some(*client_id)); - }, - Action::CloseFocus | Action::CloseTab => { - let client_id = clients.first().unwrap(); - log::debug!("Sending action to client: {}", client_id); - log::warn!("Running this action from the focused pane, can lead to unexpected behaviour."); - self.dispatch_action(action, Some(*client_id)); - }, - _ => { - // FIXME: If a specific `session_id` is specified, - // then only send the actions to that specific `client_id` - for client_id in &clients { - self.dispatch_action(action.clone(), Some(*client_id)); - } - }, - } - } - self.dispatch_action(Action::Detach, None); - self.should_exit = true; - log::error!("Quitting Now. Dispatched the actions"); - if detached { - self.exit(ExitReason::NormalDetached); - } else { - self.exit(ExitReason::Normal); - } - } - /// Dispatches an [`Action`]. /// /// This function's body dictates what each [`Action`] actually does when @@ -380,29 +323,3 @@ pub(crate) fn input_loop( ) .handle_input(); } -/// Entry point to the module. Instantiates an [`InputHandler`] and starts -/// its [`InputHandler::handle_input()`] loop. -#[allow(clippy::too_many_arguments)] -pub(crate) fn input_actions( - os_input: Box, - config: Config, - options: Options, - command_is_executing: CommandIsExecuting, - clients: Vec, - send_client_instructions: SenderWithContext, - default_mode: InputMode, - receive_input_instructions: Receiver<(InputInstruction, ErrorContext)>, - actions: Vec, - session_name: String, -) { - let _handler = InputHandler::new( - os_input, - command_is_executing, - config, - options, - send_client_instructions, - default_mode, - receive_input_instructions, - ) - .handle_actions(actions, &session_name, clients); -} diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index dcc12490ce..c1c3b7448b 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -1,12 +1,11 @@ pub mod os_input_output; -mod command_is_executing; pub mod cli_client; +mod command_is_executing; mod input_handler; -mod sessions; +pub mod old_config_converter; mod stdin_ansi_parser; mod stdin_handler; -pub mod old_config_converter; use log::info; use std::env::current_exe; @@ -144,13 +143,13 @@ pub fn start_client( envs::set_zellij("0".to_string()); config.env.set_vars(); -// let palette = config.themes.clone().map_or_else( -// || os_input.load_palette(), -// |t| { -// t.theme_config(&config_options) -// .unwrap_or_else(|| os_input.load_palette()) -// }, -// ); + // let palette = config.themes.clone().map_or_else( + // || os_input.load_palette(), + // |t| { + // t.theme_config(&config_options) + // .unwrap_or_else(|| os_input.load_palette()) + // }, + // ); let palette = config .theme_config(&config_options) .unwrap_or_else(|| os_input.load_palette()); diff --git a/zellij-client/src/old_config_converter/convert_old_yaml_files.rs b/zellij-client/src/old_config_converter/convert_old_yaml_files.rs index a68bb795bf..0d5bf2e528 100644 --- a/zellij-client/src/old_config_converter/convert_old_yaml_files.rs +++ b/zellij-client/src/old_config_converter/convert_old_yaml_files.rs @@ -1,9 +1,9 @@ +use super::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl}; +use std::path::PathBuf; use zellij_utils::{ cli::CliArgs, setup::{find_default_config_dir, get_layout_dir, get_theme_dir}, }; -use std::path::PathBuf; -use super::{config_yaml_to_config_kdl, layout_yaml_to_layout_kdl}; const OLD_CONFIG_NAME: &str = "config.yaml"; @@ -27,7 +27,12 @@ pub fn convert_old_yaml_files(opts: &CliArgs) { if let Ok(files) = std::fs::read_dir(layout_dir) { for file in files { if let Ok(file) = file { - if file.path().extension().map(|s| s.to_string_lossy().to_string()) == Some("yaml".into()) { + if file + .path() + .extension() + .map(|s| s.to_string_lossy().to_string()) + == Some("yaml".into()) + { let mut new_file_path = file.path().clone(); new_file_path.set_extension("kdl"); if !new_file_path.exists() { @@ -57,26 +62,30 @@ pub fn convert_old_yaml_files(opts: &CliArgs) { } } - - if let Some(config_dir) = config_dir { - let yaml_config_location = specified_config_location - .cloned() - .filter(|c| c.extension().map(|s| s.to_string_lossy().to_string()) == Some("yaml".into())); + let yaml_config_location = specified_config_location.cloned().filter(|c| { + c.extension().map(|s| s.to_string_lossy().to_string()) == Some("yaml".into()) + }); let specified_yaml_config_location = yaml_config_location.is_some(); - let config_location = yaml_config_location.unwrap_or_else(|| config_dir.join(OLD_CONFIG_NAME)); - match convert_yaml(config_location, layout_files_to_convert, theme_files_to_convert, specified_yaml_config_location) { + let config_location = + yaml_config_location.unwrap_or_else(|| config_dir.join(OLD_CONFIG_NAME)); + match convert_yaml( + config_location, + layout_files_to_convert, + theme_files_to_convert, + specified_yaml_config_location, + ) { Ok(should_exit) => { if should_exit { std::process::exit(0); } - } + }, Err(e) => { eprintln!(""); eprintln!("\u{1b}[1;31mFailed to convert yaml config\u{1b}[m: {}", e); eprintln!(""); std::process::exit(1); - } + }, } } } @@ -122,7 +131,9 @@ fn print_conversion_themes_message(theme_files_to_convert: Vec) { fn print_no_actions_and_wait_for_user_input() -> Result<(), String> { println!("\u{1b}[1;32mNo actions are required of you. Press ENTER to continue.\u{1b}[m"); - std::io::stdin().read_line(&mut String::new()).map_err(|e| format!("Failed to read from STDIN: {:?}", e))?; + std::io::stdin() + .read_line(&mut String::new()) + .map_err(|e| format!("Failed to read from STDIN: {:?}", e))?; Ok(()) } @@ -134,26 +145,45 @@ fn print_remain_unmodified_message(will_exit: bool) { println!(""); } -fn print_flag_help_message(layout_files_to_convert: Vec<(PathBuf, bool)>, yaml_config_file: &PathBuf, yaml_config_was_explicitly_set: bool) -> Result<(), String> { +fn print_flag_help_message( + layout_files_to_convert: Vec<(PathBuf, bool)>, + yaml_config_file: &PathBuf, + yaml_config_was_explicitly_set: bool, +) -> Result<(), String> { println!("\u{1b}[1;32mWhat do you need to do?\u{1b}[m"); - match layout_files_to_convert.iter().find(|(_f, explicit)| *explicit) { + match layout_files_to_convert + .iter() + .find(|(_f, explicit)| *explicit) + { Some((explicitly_specified_layout, _)) => { let mut kdl_config_file_path = yaml_config_file.clone(); - let mut kdl_explicitly_specified_layout= explicitly_specified_layout.clone(); + let mut kdl_explicitly_specified_layout = explicitly_specified_layout.clone(); kdl_config_file_path.set_extension("kdl"); kdl_explicitly_specified_layout.set_extension("kdl"); if yaml_config_was_explicitly_set { println!("Since both the YAML config and a YAML layout file were explicitly specified, you'll need to re-run Zellij and point it to the new files:"); println!( "\u{1b}[1;33mzellij --config {} --layout {}\u{1b}[m", - kdl_config_file_path.as_path().as_os_str().to_string_lossy().to_string(), - kdl_explicitly_specified_layout.as_path().as_os_str().to_string_lossy().to_string(), + kdl_config_file_path + .as_path() + .as_os_str() + .to_string_lossy() + .to_string(), + kdl_explicitly_specified_layout + .as_path() + .as_os_str() + .to_string_lossy() + .to_string(), ); } else { println!("Since a YAML layout was explicitly specified, you'll need to re-run Zellij and point it to the new layout:"); println!( "\u{1b}[1;33mzellij --layout {}\u{1b}[m", - kdl_explicitly_specified_layout.as_path().as_os_str().to_string_lossy().to_string(), + kdl_explicitly_specified_layout + .as_path() + .as_os_str() + .to_string_lossy() + .to_string(), ); } }, @@ -164,65 +194,110 @@ fn print_flag_help_message(layout_files_to_convert: Vec<(PathBuf, bool)>, yaml_c println!("Since the YAML config was explicitly specified, you'll need to re-run Zellij and point it to the new config:"); println!( "\u{1b}[1;33mzellij --config {}\u{1b}[m", - kdl_config_file_path.as_path().as_os_str().to_string_lossy().to_string(), + kdl_config_file_path + .as_path() + .as_os_str() + .to_string_lossy() + .to_string(), ); } - } + }, } println!(""); println!("\u{1b}[1;32mPress ENTER to continue.\u{1b}[m"); - std::io::stdin().read_line(&mut String::new()).map_err(|e| format!("Failed to read from STDIN: {:?}", e))?; + std::io::stdin() + .read_line(&mut String::new()) + .map_err(|e| format!("Failed to read from STDIN: {:?}", e))?; Ok(()) } fn convert_layouts(layout_files_to_convert: Vec<(PathBuf, bool)>) -> Result<(), String> { for (layout_file, _was_explicitly_set) in layout_files_to_convert { - let raw_layout_file = std::fs::read_to_string(&layout_file).map_err(|e| format!("Failed to read layout file {:?}: {:?}", layout_file, e))?; + let raw_layout_file = std::fs::read_to_string(&layout_file) + .map_err(|e| format!("Failed to read layout file {:?}: {:?}", layout_file, e))?; let kdl_layout = layout_yaml_to_layout_kdl(&raw_layout_file)?; let mut new_layout_file = layout_file.clone(); new_layout_file.set_extension("kdl"); - std::fs::write(&new_layout_file, kdl_layout).map_err(|e| format!("Failed to write new layout file to {:?}: {:?}", new_layout_file, e))?; + std::fs::write(&new_layout_file, kdl_layout).map_err(|e| { + format!( + "Failed to write new layout file to {:?}: {:?}", + new_layout_file, e + ) + })?; } Ok(()) } fn convert_themes(theme_files_to_convert: Vec) -> Result<(), String> { for theme_file in theme_files_to_convert { - let raw_theme_file = std::fs::read_to_string(&theme_file).map_err(|e| format!("Failed to read theme file {:?}: {:?}", theme_file, e))?; + let raw_theme_file = std::fs::read_to_string(&theme_file) + .map_err(|e| format!("Failed to read theme file {:?}: {:?}", theme_file, e))?; let kdl_theme = config_yaml_to_config_kdl(&raw_theme_file, true)?; let mut new_theme_file = theme_file.clone(); new_theme_file.set_extension("kdl"); - std::fs::write(&new_theme_file, kdl_theme).map_err(|e| format!("Failed to write new theme file to {:?}: {:?}", new_theme_file, e))?; + std::fs::write(&new_theme_file, kdl_theme).map_err(|e| { + format!( + "Failed to write new theme file to {:?}: {:?}", + new_theme_file, e + ) + })?; } Ok(()) } fn convert_config(yaml_config_file: PathBuf, new_config_file: PathBuf) -> Result<(), String> { if yaml_config_file.exists() && !new_config_file.exists() { - let raw_config_file = std::fs::read_to_string(&yaml_config_file).map_err(|e| format!("Failed to read config file {:?}: {:?}", yaml_config_file, e))?; + let raw_config_file = std::fs::read_to_string(&yaml_config_file) + .map_err(|e| format!("Failed to read config file {:?}: {:?}", yaml_config_file, e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; - std::fs::write(&new_config_file, kdl_config).map_err(|e| format!("Failed to write new config file to {:?}: {:?}", new_config_file, e))?; + std::fs::write(&new_config_file, kdl_config).map_err(|e| { + format!( + "Failed to write new config file to {:?}: {:?}", + new_config_file, e + ) + })?; } Ok(()) } -fn convert_yaml(yaml_config_file: PathBuf, layout_files_to_convert: Vec<(PathBuf, bool)>, theme_files_to_convert: Vec, yaml_config_was_explicitly_set: bool) -> Result { +fn convert_yaml( + yaml_config_file: PathBuf, + layout_files_to_convert: Vec<(PathBuf, bool)>, + theme_files_to_convert: Vec, + yaml_config_was_explicitly_set: bool, +) -> Result { let mut should_exit = false; let mut new_config_file = yaml_config_file.clone(); new_config_file.set_extension("kdl"); let yaml_config_file_exists = yaml_config_file.exists(); - let layout_was_explicitly_set = layout_files_to_convert.iter().find(|(_l, was_explicitly_set)| *was_explicitly_set).is_some(); + let layout_was_explicitly_set = layout_files_to_convert + .iter() + .find(|(_l, was_explicitly_set)| *was_explicitly_set) + .is_some(); let new_config_file_exists = new_config_file.exists(); - let no_need_to_convert_config = (new_config_file_exists && !yaml_config_was_explicitly_set) || !yaml_config_file_exists; - if no_need_to_convert_config && layout_files_to_convert.is_empty() && theme_files_to_convert.is_empty() && !layout_was_explicitly_set { + let no_need_to_convert_config = + (new_config_file_exists && !yaml_config_was_explicitly_set) || !yaml_config_file_exists; + if no_need_to_convert_config + && layout_files_to_convert.is_empty() + && theme_files_to_convert.is_empty() + && !layout_was_explicitly_set + { // Nothing to do... return Ok(should_exit); } print_conversion_title_message(); if yaml_config_file_exists && !new_config_file_exists { print_converting_config_message( - yaml_config_file.as_path().as_os_str().to_string_lossy().to_string(), - new_config_file.as_path().as_os_str().to_string_lossy().to_string() + yaml_config_file + .as_path() + .as_os_str() + .to_string_lossy() + .to_string(), + new_config_file + .as_path() + .as_os_str() + .to_string_lossy() + .to_string(), ); } else if yaml_config_file_exists && new_config_file_exists && yaml_config_was_explicitly_set { return Err( @@ -241,7 +316,11 @@ fn convert_yaml(yaml_config_file: PathBuf, layout_files_to_convert: Vec<(PathBuf } print_remain_unmodified_message(layout_was_explicitly_set || yaml_config_was_explicitly_set); if layout_was_explicitly_set || yaml_config_was_explicitly_set { - print_flag_help_message(layout_files_to_convert.clone(), &yaml_config_file, yaml_config_was_explicitly_set)?; + print_flag_help_message( + layout_files_to_convert.clone(), + &yaml_config_file, + yaml_config_was_explicitly_set, + )?; should_exit = true; } else { print_no_actions_and_wait_for_user_input()?; diff --git a/zellij-client/src/old_config_converter/mod.rs b/zellij-client/src/old_config_converter/mod.rs index d120562d8b..ac6d767628 100644 --- a/zellij-client/src/old_config_converter/mod.rs +++ b/zellij-client/src/old_config_converter/mod.rs @@ -1,6 +1,6 @@ +mod convert_old_yaml_files; mod old_config; mod old_layout; -mod convert_old_yaml_files; +pub use convert_old_yaml_files::convert_old_yaml_files; pub use old_config::config_yaml_to_config_kdl; pub use old_layout::layout_yaml_to_layout_kdl; -pub use convert_old_yaml_files::convert_old_yaml_files; diff --git a/zellij-client/src/old_config_converter/old_config.rs b/zellij-client/src/old_config_converter/old_config.rs index ce8bd407af..73e098b7d6 100644 --- a/zellij-client/src/old_config_converter/old_config.rs +++ b/zellij-client/src/old_config_converter/old_config.rs @@ -5,10 +5,10 @@ use std::fmt; use std::path::PathBuf; -use url::Url; -use serde::{Deserialize, Deserializer, Serialize}; use serde::de::{Error, Visitor}; -use std::collections::{HashMap, BTreeMap}; +use serde::{Deserialize, Deserializer, Serialize}; +use std::collections::{BTreeMap, HashMap}; +use url::Url; const ON_FORCE_CLOSE_DESCRIPTION: &'static str = " // Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP @@ -125,7 +125,6 @@ const LAYOUT_DIR_DESCRIPTION: &'static str = " // "; - const THEME_DIR_DESCRIPTION: &'static str = " // The folder in which Zellij will look for themes // @@ -156,37 +155,116 @@ fn options_yaml_to_options_kdl(options_yaml: &OldOptions, no_comments: bool) -> if !no_comments { options_kdl.push_str(&format!($absent_pattern)); } - } + }, }; if !no_comments || options_yaml.$attribute_name.is_some() { options_kdl.push('\n'); } - } + }; } - push_option!(on_force_close, ON_FORCE_CLOSE_DESCRIPTION, "on_force_close \"{}\"", "// on_force_close \"quit\""); - push_option!(simplified_ui, SIMPLIFIED_UI_DESCRIPTION, "simplified_ui {}", "// simplified_ui true"); - push_option!(default_shell, DEFAULT_SHELL_DESCRIPTION, "default_shell {:?}", "// default_shell \"fish\""); - push_option!(pane_frames, PANE_FRAMES_DESCRIPTION, "pane_frames {}", "// pane_frames true"); - push_option!(theme, DEFAULT_THEME_DESCRIPTION, "theme {:?} ", "// theme \"default\""); - push_option!(default_layout, DEFAULT_LAYOUT_DESCRIPTION, "default_layout {:?}", "// default_layout \"compact\""); - push_option!(default_mode, DEFAULT_MODE_DESCRIPTION, "default_mode \"{}\"", "// default_mode \"locked\""); - push_option!(mouse_mode, MOUSE_MODE_DESCRIPTION, "mouse_mode {}", "// mouse_mode false"); - push_option!(scroll_buffer_size, SCROLL_BUFFER_SIZE_DESCRIPTION, "scroll_buffer_size {}", "// scroll_buffer_size 10000"); + push_option!( + on_force_close, + ON_FORCE_CLOSE_DESCRIPTION, + "on_force_close \"{}\"", + "// on_force_close \"quit\"" + ); + push_option!( + simplified_ui, + SIMPLIFIED_UI_DESCRIPTION, + "simplified_ui {}", + "// simplified_ui true" + ); + push_option!( + default_shell, + DEFAULT_SHELL_DESCRIPTION, + "default_shell {:?}", + "// default_shell \"fish\"" + ); + push_option!( + pane_frames, + PANE_FRAMES_DESCRIPTION, + "pane_frames {}", + "// pane_frames true" + ); + push_option!( + theme, + DEFAULT_THEME_DESCRIPTION, + "theme {:?} ", + "// theme \"default\"" + ); + push_option!( + default_layout, + DEFAULT_LAYOUT_DESCRIPTION, + "default_layout {:?}", + "// default_layout \"compact\"" + ); + push_option!( + default_mode, + DEFAULT_MODE_DESCRIPTION, + "default_mode \"{}\"", + "// default_mode \"locked\"" + ); + push_option!( + mouse_mode, + MOUSE_MODE_DESCRIPTION, + "mouse_mode {}", + "// mouse_mode false" + ); + push_option!( + scroll_buffer_size, + SCROLL_BUFFER_SIZE_DESCRIPTION, + "scroll_buffer_size {}", + "// scroll_buffer_size 10000" + ); push_option!(copy_command, COPY_COMMAND_DESCRIPTION, "copy_command {:?}"); - push_option!(copy_clipboard, COPY_CLIPBOARD_DESCRIPTION, "copy_clipboard \"{}\"", "// copy_clipboard \"primary\""); - push_option!(copy_on_select, COPY_ON_SELECT_DESCRIPTION, "copy_on_select {}", "// copy_on_select false"); - push_option!(scrollback_editor, SCROLLBACK_EDITOR_DESCRIPTION, "scrollback_editor {:?}", "// scrollback_editor \"/usr/bin/vim\""); - push_option!(mirror_session, MIRROR_SESSION_DESCRIPTION, "mirror_session {}", "// mirror_session true"); - push_option!(layout_dir, LAYOUT_DIR_DESCRIPTION, "layout_dir {:?}", "// layout_dir /path/to/my/layout_dir"); - push_option!(theme_dir, THEME_DIR_DESCRIPTION, "theme_dir {:?}", "// theme_dir \"/path/to/my/theme_dir\""); + push_option!( + copy_clipboard, + COPY_CLIPBOARD_DESCRIPTION, + "copy_clipboard \"{}\"", + "// copy_clipboard \"primary\"" + ); + push_option!( + copy_on_select, + COPY_ON_SELECT_DESCRIPTION, + "copy_on_select {}", + "// copy_on_select false" + ); + push_option!( + scrollback_editor, + SCROLLBACK_EDITOR_DESCRIPTION, + "scrollback_editor {:?}", + "// scrollback_editor \"/usr/bin/vim\"" + ); + push_option!( + mirror_session, + MIRROR_SESSION_DESCRIPTION, + "mirror_session {}", + "// mirror_session true" + ); + push_option!( + layout_dir, + LAYOUT_DIR_DESCRIPTION, + "layout_dir {:?}", + "// layout_dir /path/to/my/layout_dir" + ); + push_option!( + theme_dir, + THEME_DIR_DESCRIPTION, + "theme_dir {:?}", + "// theme_dir \"/path/to/my/theme_dir\"" + ); options_kdl } fn env_yaml_to_env_kdl(env_yaml: &OldEnvironmentVariablesFromYaml) -> String { let mut env_kdl = String::new(); - let mut env_vars: Vec<(String, String)> = env_yaml.env.iter().map(|(name, val)| (name.clone(), val.clone())).collect(); + let mut env_vars: Vec<(String, String)> = env_yaml + .env + .iter() + .map(|(name, val)| (name.clone(), val.clone())) + .collect(); env_vars.sort_unstable(); env_kdl.push_str("env {\n"); for (name, val) in env_vars { @@ -204,9 +282,15 @@ fn plugins_yaml_to_plugins_kdl(plugins_yaml_to_plugins_kdl: &OldPluginsConfigFro } for plugin_config in &plugins_yaml_to_plugins_kdl.0 { if plugin_config._allow_exec_host_cmd { - plugins_kdl.push_str(&format!(" {} {{ path {:?}; _allow_exec_host_cmd true; }}\n", plugin_config.tag.0, plugin_config.path)); + plugins_kdl.push_str(&format!( + " {} {{ path {:?}; _allow_exec_host_cmd true; }}\n", + plugin_config.tag.0, plugin_config.path + )); } else { - plugins_kdl.push_str(&format!(" {} {{ path {:?}; }}\n", plugin_config.tag.0, plugin_config.path)); + plugins_kdl.push_str(&format!( + " {} {{ path {:?}; }}\n", + plugin_config.tag.0, plugin_config.path + )); } } if !&plugins_yaml_to_plugins_kdl.0.is_empty() { @@ -236,28 +320,37 @@ fn ui_config_yaml_to_ui_config_kdl(ui_config_yaml: &OldUiConfigFromYaml) -> Stri kdl_ui_config } -fn theme_config_yaml_to_theme_config_kdl(theme_config_yaml: &OldThemesFromYamlIntermediate) -> String { +fn theme_config_yaml_to_theme_config_kdl( + theme_config_yaml: &OldThemesFromYamlIntermediate, +) -> String { macro_rules! theme_color { ($theme:ident, $color:ident, $color_name:expr, $kdl_theme_config:expr) => { match $theme.palette.$color { OldPaletteColorFromYaml::Rgb((r, g, b)) => { - $kdl_theme_config.push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b)); - } + $kdl_theme_config + .push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b)); + }, OldPaletteColorFromYaml::EightBit(eight_bit_color) => { - $kdl_theme_config.push_str(&format!(" {} {}\n", $color_name, eight_bit_color)); - } + $kdl_theme_config + .push_str(&format!(" {} {}\n", $color_name, eight_bit_color)); + }, OldPaletteColorFromYaml::Hex(OldHexColor(r, g, b)) => { - $kdl_theme_config.push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b)); - } + $kdl_theme_config + .push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b)); + }, } - } + }; } let mut kdl_theme_config = String::new(); if !theme_config_yaml.0.is_empty() { kdl_theme_config.push_str("themes {\n") } - let mut themes: Vec<(String, OldTheme)> = theme_config_yaml.0.iter().map(|(theme_name, theme)| (theme_name.clone(), theme.clone())).collect(); + let mut themes: Vec<(String, OldTheme)> = theme_config_yaml + .0 + .iter() + .map(|(theme_name, theme)| (theme_name.clone(), theme.clone())) + .collect(); themes.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); for (theme_name, theme) in themes { kdl_theme_config.push_str(&format!(" {} {{\n", theme_name)); @@ -304,16 +397,20 @@ fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String match &keybinds_yaml.unbind { OldUnbind::Keys(keys_to_unbind) => { kdl_keybinds.push_str("keybinds {\n"); - let key_string: String = keys_to_unbind.iter().map(|k| format!("\"{}\"", k)).collect::>().join(" "); + let key_string: String = keys_to_unbind + .iter() + .map(|k| format!("\"{}\"", k)) + .collect::>() + .join(" "); kdl_keybinds.push_str(&format!(" unbind {}\n", key_string)); - } + }, OldUnbind::All(should_unbind_all_defaults) => { if *should_unbind_all_defaults { kdl_keybinds.push_str("keybinds clear-defaults=true {\n"); } else { kdl_keybinds.push_str("keybinds {\n"); } - } + }, } for mode in modes { @@ -325,23 +422,36 @@ fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String OldKeyActionUnbind::KeyAction(key_action) => { let keys = &key_action.key; let actions = &key_action.action; - let key_string: String = keys.iter().map(|k| format!("\"{}\"", k)).collect::>().join(" "); - let actions_string: String = actions.iter().map(|a| format!("{};", a)).collect::>().join(" "); - kdl_mode_keybinds.push_str(&format!(" bind {} {{ {} }}\n", key_string, actions_string)); - } - OldKeyActionUnbind::Unbind(unbind) => { - match &unbind.unbind { - OldUnbind::Keys(keys_to_unbind) => { - let key_string: String = keys_to_unbind.iter().map(|k| format!("\"{}\"", k)).collect::>().join(" "); - kdl_mode_keybinds.push_str(&format!(" unbind {}\n", key_string)); - }, - OldUnbind::All(unbind_all) => { - if *unbind_all { - should_clear_mode_defaults = true; - } + let key_string: String = keys + .iter() + .map(|k| format!("\"{}\"", k)) + .collect::>() + .join(" "); + let actions_string: String = actions + .iter() + .map(|a| format!("{};", a)) + .collect::>() + .join(" "); + kdl_mode_keybinds.push_str(&format!( + " bind {} {{ {} }}\n", + key_string, actions_string + )); + }, + OldKeyActionUnbind::Unbind(unbind) => match &unbind.unbind { + OldUnbind::Keys(keys_to_unbind) => { + let key_string: String = keys_to_unbind + .iter() + .map(|k| format!("\"{}\"", k)) + .collect::>() + .join(" "); + kdl_mode_keybinds.push_str(&format!(" unbind {}\n", key_string)); + }, + OldUnbind::All(unbind_all) => { + if *unbind_all { + should_clear_mode_defaults = true; } - } - } + }, + }, } } if should_clear_mode_defaults { @@ -357,36 +467,32 @@ fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String kdl_keybinds } -pub fn config_yaml_to_config_kdl(raw_yaml_config: &str, no_comments: bool) -> Result { // returns the raw kdl config - let config_from_yaml: OldConfigFromYaml = serde_yaml::from_str(raw_yaml_config).map_err(|e| format!("Failed to parse yaml: {:?}", e))?; +pub fn config_yaml_to_config_kdl( + raw_yaml_config: &str, + no_comments: bool, +) -> Result { + // returns the raw kdl config + let config_from_yaml: OldConfigFromYaml = serde_yaml::from_str(raw_yaml_config) + .map_err(|e| format!("Failed to parse yaml: {:?}", e))?; let mut kdl_config = String::new(); if let Some(old_config_keybinds) = config_from_yaml.keybinds.as_ref() { - kdl_config.push_str( - &keybinds_yaml_to_keybinds_kdl(old_config_keybinds) - ); + kdl_config.push_str(&keybinds_yaml_to_keybinds_kdl(old_config_keybinds)); } if let Some(old_config_options) = config_from_yaml.options.as_ref() { - kdl_config.push_str( - &options_yaml_to_options_kdl(old_config_options, no_comments) - ); + kdl_config.push_str(&options_yaml_to_options_kdl( + old_config_options, + no_comments, + )); } if let Some(old_config_env_variables) = config_from_yaml.env.as_ref() { - kdl_config.push_str( - &env_yaml_to_env_kdl(old_config_env_variables) - ); + kdl_config.push_str(&env_yaml_to_env_kdl(old_config_env_variables)); } - kdl_config.push_str( - &plugins_yaml_to_plugins_kdl(&config_from_yaml.plugins) - ); + kdl_config.push_str(&plugins_yaml_to_plugins_kdl(&config_from_yaml.plugins)); if let Some(old_ui_config) = config_from_yaml.ui.as_ref() { - kdl_config.push_str( - &ui_config_yaml_to_ui_config_kdl(old_ui_config) - ); + kdl_config.push_str(&ui_config_yaml_to_ui_config_kdl(old_ui_config)); } if let Some(old_themes_config) = config_from_yaml.themes.as_ref() { - kdl_config.push_str( - &theme_config_yaml_to_theme_config_kdl(old_themes_config) - ); + kdl_config.push_str(&theme_config_yaml_to_theme_config_kdl(old_themes_config)); } Ok(kdl_config) } @@ -711,7 +817,6 @@ pub struct OldOptions { pub scrollback_editor: Option, } - /// Describes the different input modes, which change the way that keystrokes will be interpreted. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] pub enum OldInputMode { @@ -823,20 +928,18 @@ impl std::fmt::Display for OldKey { '\n' => write!(f, "Enter"), '\t' => write!(f, "Tab"), '\"' => write!(f, "\\\""), // make sure it is escaped because otherwise it will be - // seen as a KDL string starter/terminator + // seen as a KDL string starter/terminator ' ' => write!(f, "Space"), _ => write!(f, "{}", c), }, Self::Alt(char_or_arrow) => match char_or_arrow { OldCharOrArrow::Char(c) => write!(f, "Alt {}", c), - OldCharOrArrow::Direction(direction) => { - match direction { - OldDirection::Left => write!(f, "Alt Left"), - OldDirection::Right => write!(f, "Alt Right"), - OldDirection::Up => write!(f, "Alt Up"), - OldDirection::Down => write!(f, "Alt Down"), - } - } + OldCharOrArrow::Direction(direction) => match direction { + OldDirection::Left => write!(f, "Alt Left"), + OldDirection::Right => write!(f, "Alt Right"), + OldDirection::Up => write!(f, "Alt Up"), + OldDirection::Down => write!(f, "Alt Down"), + }, }, Self::Ctrl(c) => write!(f, "Ctrl {}", c), Self::BackTab => write!(f, "Tab"), @@ -945,9 +1048,17 @@ impl std::fmt::Display for OldAction { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { match self { Self::Quit => write!(f, "Quit"), - Self::Write(bytes) => write!(f, "Write {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::Write(bytes) => write!( + f, + "Write {}", + bytes + .iter() + .map(|c| format!("{}", *c)) + .collect::>() + .join(" ") + ), Self::WriteChars(chars) => write!(f, "WriteChars \"{}\"", chars), - Self::SwitchToMode(input_mode) => write!(f, "SwitchToMode \"{}\"", input_mode), + Self::SwitchToMode(input_mode) => write!(f, "SwitchToMode \"{}\"", input_mode), Self::Resize(resize_direction) => write!(f, "Resize \"{}\"", resize_direction), Self::FocusNextPane => write!(f, "FocusNextPane"), Self::FocusPreviousPane => write!(f, "FocusPreviousPane"), @@ -977,7 +1088,15 @@ impl std::fmt::Display for OldAction { Self::TogglePaneEmbedOrFloating => write!(f, "TogglePaneEmbedOrFloating"), Self::ToggleFloatingPanes => write!(f, "ToggleFloatingPanes"), Self::CloseFocus => write!(f, "CloseFocus"), - Self::PaneNameInput(bytes) => write!(f, "PaneNameInput {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::PaneNameInput(bytes) => write!( + f, + "PaneNameInput {}", + bytes + .iter() + .map(|c| format!("{}", *c)) + .collect::>() + .join(" ") + ), Self::UndoRenamePane => write!(f, "UndoRenamePane"), Self::NewTab(_) => write!(f, "NewTab"), Self::NoOp => write!(f, "NoOp"), @@ -987,35 +1106,55 @@ impl std::fmt::Display for OldAction { Self::GoToTab(index) => write!(f, "GoToTab {}", index), Self::ToggleTab => write!(f, "ToggleTab"), // Self::TabNameInput(bytes) => write!(f, "TabNameInput {}", format!("{}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" "))), - Self::TabNameInput(bytes) => write!(f, "TabNameInput {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::TabNameInput(bytes) => write!( + f, + "TabNameInput {}", + bytes + .iter() + .map(|c| format!("{}", *c)) + .collect::>() + .join(" ") + ), Self::UndoRenameTab => write!(f, "UndoRenameTab"), - Self::Run(run_command_action) => { + Self::Run(run_command_action) => { let mut run_block_serialized = format!("Run {:?}", run_command_action.command); for arg in &run_command_action.args { run_block_serialized.push_str(&format!(" \"{}\"", arg)); } match (&run_command_action.cwd, run_command_action.direction) { (Some(cwd), Some(direction)) => { - run_block_serialized.push_str(&format!("{{ cwd {:?}; direction \"{}\"; }}", cwd, direction)); - } + run_block_serialized.push_str(&format!( + "{{ cwd {:?}; direction \"{}\"; }}", + cwd, direction + )); + }, (None, Some(direction)) => { - run_block_serialized.push_str(&format!("{{ direction \"{}\"; }}", direction)); - } + run_block_serialized + .push_str(&format!("{{ direction \"{}\"; }}", direction)); + }, (Some(cwd), None) => { run_block_serialized.push_str(&format!("{{ cwd {:?}; }}", cwd)); - } - (None, None) => {} + }, + (None, None) => {}, } write!(f, "{}", run_block_serialized) - } + }, Self::Detach => write!(f, "Detach"), Self::Copy => write!(f, "Copy"), Self::Confirm => write!(f, "Confirm"), Self::Deny => write!(f, "Deny"), - Self::SearchInput(bytes) => write!(f, "SearchInput {}", bytes.iter().map(|c| format!("{}", *c)).collect::>().join(" ")), + Self::SearchInput(bytes) => write!( + f, + "SearchInput {}", + bytes + .iter() + .map(|c| format!("{}", *c)) + .collect::>() + .join(" ") + ), Self::Search(direction) => write!(f, "Search \"{}\"", direction), Self::SearchToggleOption(option) => write!(f, "SearchToggleOption \"{}\"", option), - _ => Err(std::fmt::Error) + _ => Err(std::fmt::Error), } } } diff --git a/zellij-client/src/old_config_converter/old_layout.rs b/zellij-client/src/old_config_converter/old_layout.rs index fabe95ab9a..cd1aa66fb3 100644 --- a/zellij-client/src/old_config_converter/old_layout.rs +++ b/zellij-client/src/old_config_converter/old_layout.rs @@ -2,18 +2,18 @@ // // It is supposed to be mostly self containing - please refrain from adding to it, importing // from it or changing it -use super::{ - old_config::{OldConfigFromYaml, OldRunCommand, config_yaml_to_config_kdl}, -}; +use super::old_config::{config_yaml_to_config_kdl, OldConfigFromYaml, OldRunCommand}; use serde::{Deserialize, Serialize}; use std::vec::Vec; -use std::{ - fmt, - path::PathBuf, -}; +use std::{fmt, path::PathBuf}; use url::Url; -fn pane_line(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool) -> String { +fn pane_line( + pane_name: Option<&String>, + split_size: Option, + focus: Option, + borderless: bool, +) -> String { let mut pane_line = format!("pane"); if let Some(pane_name) = pane_name { pane_line.push_str(&format!(" name=\"{}\"", pane_name)); @@ -30,7 +30,12 @@ fn pane_line(pane_name: Option<&String>, split_size: Option, focus pane_line } -fn tab_line(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool) -> String { +fn tab_line( + pane_name: Option<&String>, + split_size: Option, + focus: Option, + borderless: bool, +) -> String { let mut pane_line = format!("tab"); if let Some(pane_name) = pane_name { pane_line.push_str(&format!(" name=\"{}\"", pane_name)); @@ -47,7 +52,13 @@ fn tab_line(pane_name: Option<&String>, split_size: Option, focus: pane_line } -fn pane_line_with_children(pane_name: Option<&String>, split_size: Option, focus: Option, borderless: bool, split_direction: OldDirection) -> String { +fn pane_line_with_children( + pane_name: Option<&String>, + split_size: Option, + focus: Option, + borderless: bool, + split_direction: OldDirection, +) -> String { let mut pane_line = format!("pane"); if let Some(pane_name) = pane_name { pane_line.push_str(&format!(" name=\"{}\"", pane_name)); @@ -65,7 +76,13 @@ fn pane_line_with_children(pane_name: Option<&String>, split_size: Option, split_size: Option, focus: Option, borderless: bool, command: &PathBuf) -> String { +fn pane_command_line( + pane_name: Option<&String>, + split_size: Option, + focus: Option, + borderless: bool, + command: &PathBuf, +) -> String { let mut pane_line = format!("pane command={:?}", command); if let Some(pane_name) = pane_name { pane_line.push_str(&format!(" name=\"{}\"", pane_name)); @@ -82,7 +99,13 @@ fn pane_command_line(pane_name: Option<&String>, split_size: Option, split_size: Option, focus: Option, borderless: bool, split_direction: OldDirection) -> String { +fn tab_line_with_children( + pane_name: Option<&String>, + split_size: Option, + focus: Option, + borderless: bool, + split_direction: OldDirection, +) -> String { let mut pane_line = format!("tab"); if let Some(pane_name) = pane_name { pane_line.push_str(&format!(" name=\"{}\"", pane_name)); @@ -100,8 +123,17 @@ fn tab_line_with_children(pane_name: Option<&String>, split_size: Option String { - let mut stringified = if is_base { String::new() } else { String::from("\n") }; +fn stringify_template( + template: &OldLayoutTemplate, + indentation: String, + has_no_tabs: bool, + is_base: bool, +) -> String { + let mut stringified = if is_base { + String::new() + } else { + String::from("\n") + }; if is_base && !template.parts.is_empty() && template.direction == OldDirection::Vertical { // we don't support specifying the split direction in the layout node // eg. layout split_direction="Vertical" { .. } <== this is not supported!! @@ -110,14 +142,34 @@ fn stringify_template(template: &OldLayoutTemplate, indentation: String, has_no_ // pane split_direction="Vertical" { .. } // } let child_indentation = format!("{} ", &indentation); - stringified.push_str(&stringify_template(template, child_indentation, has_no_tabs, false)); + stringified.push_str(&stringify_template( + template, + child_indentation, + has_no_tabs, + false, + )); } else if !template.parts.is_empty() { if !is_base { - stringified.push_str(&format!("{}{} {{", indentation, pane_line_with_children(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless, template.direction))); + stringified.push_str(&format!( + "{}{} {{", + indentation, + pane_line_with_children( + template.pane_name.as_ref(), + template.split_size, + template.focus, + template.borderless, + template.direction + ) + )); } for part in &template.parts { let child_indentation = format!("{} ", &indentation); - stringified.push_str(&stringify_template(&part, child_indentation, has_no_tabs, false)); + stringified.push_str(&stringify_template( + &part, + child_indentation, + has_no_tabs, + false, + )); } if !is_base { stringified.push_str(&format!("\n{}}}", indentation)); @@ -127,24 +179,64 @@ fn stringify_template(template: &OldLayoutTemplate, indentation: String, has_no_ } else { match template.run.as_ref() { Some(OldRunFromYaml::Plugin(plugin_from_yaml)) => { - stringified.push_str(&format!("{}{} {{\n", &indentation, pane_line(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless))); - stringified.push_str(&format!("{} plugin location=\"{}\"\n", &indentation, plugin_from_yaml.location)); + stringified.push_str(&format!( + "{}{} {{\n", + &indentation, + pane_line( + template.pane_name.as_ref(), + template.split_size, + template.focus, + template.borderless + ) + )); + stringified.push_str(&format!( + "{} plugin location=\"{}\"\n", + &indentation, plugin_from_yaml.location + )); stringified.push_str(&format!("{}}}", &indentation)); }, Some(OldRunFromYaml::Command(command_from_yaml)) => { - stringified.push_str(&format!("{}{}", &indentation, &pane_command_line(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless, &command_from_yaml.command))); + stringified.push_str(&format!( + "{}{}", + &indentation, + &pane_command_line( + template.pane_name.as_ref(), + template.split_size, + template.focus, + template.borderless, + &command_from_yaml.command + ) + )); if let Some(cwd) = command_from_yaml.cwd.as_ref() { stringified.push_str(&format!(" cwd={:?}", cwd)); } if !command_from_yaml.args.is_empty() { stringified.push_str(" {\n"); - stringified.push_str(&format!("{} args {}\n", &indentation, command_from_yaml.args.iter().map(|s| format!("\"{}\"", s)).collect::>().join(" "))); + stringified.push_str(&format!( + "{} args {}\n", + &indentation, + command_from_yaml + .args + .iter() + .map(|s| format!("\"{}\"", s)) + .collect::>() + .join(" ") + )); stringified.push_str(&format!("{}}}", &indentation)); } }, None => { - stringified.push_str(&format!("{}{}", &indentation, pane_line(template.pane_name.as_ref(), template.split_size, template.focus, template.borderless))); - } + stringified.push_str(&format!( + "{}{}", + &indentation, + pane_line( + template.pane_name.as_ref(), + template.split_size, + template.focus, + template.borderless + ) + )); + }, }; } stringified @@ -155,31 +247,68 @@ fn stringify_tabs(tabs: Vec) -> String { for tab in tabs { let child_indentation = String::from(" "); if !tab.parts.is_empty() { - stringified.push_str(&format!("\n{}{} {{", child_indentation, tab_line_with_children(tab.pane_name.as_ref(), tab.split_size, tab.focus, tab.borderless, tab.direction))); + stringified.push_str(&format!( + "\n{}{} {{", + child_indentation, + tab_line_with_children( + tab.pane_name.as_ref(), + tab.split_size, + tab.focus, + tab.borderless, + tab.direction + ) + )); let tab_template = OldLayoutTemplate::from(tab); - stringified.push_str(&stringify_template(&tab_template, child_indentation.clone(), true, true)); + stringified.push_str(&stringify_template( + &tab_template, + child_indentation.clone(), + true, + true, + )); stringified.push_str(&format!("\n{}}}", child_indentation)); } else { - stringified.push_str(&format!("\n{}{}", child_indentation, tab_line(tab.pane_name.as_ref(), tab.split_size, tab.focus, tab.borderless))); + stringified.push_str(&format!( + "\n{}{}", + child_indentation, + tab_line( + tab.pane_name.as_ref(), + tab.split_size, + tab.focus, + tab.borderless + ) + )); } } stringified } -pub fn layout_yaml_to_layout_kdl(raw_yaml_layout: &str) -> Result { // returns the raw kdl config - let layout_from_yaml: OldLayoutFromYamlIntermediate = serde_yaml::from_str(raw_yaml_layout).map_err(|e| format!("Failed to parse yaml: {:?}", e))?; +pub fn layout_yaml_to_layout_kdl(raw_yaml_layout: &str) -> Result { + // returns the raw kdl config + let layout_from_yaml: OldLayoutFromYamlIntermediate = serde_yaml::from_str(raw_yaml_layout) + .map_err(|e| format!("Failed to parse yaml: {:?}", e))?; let mut kdl_layout = String::new(); kdl_layout.push_str("layout {"); let template = layout_from_yaml.template; let tabs = layout_from_yaml.tabs; - let has_no_tabs = tabs.is_empty() || tabs.len() == 1 && tabs.get(0).map(|t| t.parts.is_empty()).unwrap_or(false); + let has_no_tabs = tabs.is_empty() + || tabs.len() == 1 && tabs.get(0).map(|t| t.parts.is_empty()).unwrap_or(false); if has_no_tabs { let indentation = String::from(""); - kdl_layout.push_str(&stringify_template(&template, indentation, has_no_tabs, true)); + kdl_layout.push_str(&stringify_template( + &template, + indentation, + has_no_tabs, + true, + )); } else { kdl_layout.push_str("\n default_tab_template {"); let indentation = String::from(" "); - kdl_layout.push_str(&stringify_template(&template, indentation, has_no_tabs, true)); + kdl_layout.push_str(&stringify_template( + &template, + indentation, + has_no_tabs, + true, + )); kdl_layout.push_str("\n }"); kdl_layout.push_str(&stringify_tabs(tabs)); } @@ -195,7 +324,7 @@ pub fn layout_yaml_to_layout_kdl(raw_yaml_layout: &str) -> Result Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -16,10 +21,15 @@ fn properly_convert_default_config() -> Result<(), String> { #[test] fn convert_config_with_custom_options() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -27,10 +37,15 @@ fn convert_config_with_custom_options() -> Result<(), String> { #[test] fn convert_config_with_keybind_unbinds_in_mode() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -41,7 +56,9 @@ fn convert_config_with_global_keybind_unbinds() -> Result<(), String> { let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml", env!("CARGO_MANIFEST_DIR"))); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -52,7 +69,9 @@ fn convert_config_with_unbind_all_keys_per_mode() -> Result<(), String> { let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml", env!("CARGO_MANIFEST_DIR"))); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -60,10 +79,15 @@ fn convert_config_with_unbind_all_keys_per_mode() -> Result<(), String> { #[test] fn convert_config_with_env_variables() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -71,10 +95,15 @@ fn convert_config_with_env_variables() -> Result<(), String> { #[test] fn convert_config_with_ui_config() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -82,10 +111,15 @@ fn convert_config_with_ui_config() -> Result<(), String> { #[test] fn convert_config_with_themes_config() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = config_yaml_to_config_kdl(&raw_config_file, false)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) diff --git a/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs b/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs index 6b9650ccdb..30e689bb13 100644 --- a/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs +++ b/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs @@ -1,14 +1,19 @@ use crate::old_config_converter::layout_yaml_to_layout_kdl; +use insta::assert_snapshot; use std::path::PathBuf; use std::{fs::File, io::prelude::*}; -use insta::assert_snapshot; #[test] fn properly_convert_default_layout() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -16,10 +21,15 @@ fn properly_convert_default_layout() -> Result<(), String> { #[test] fn properly_convert_layout_with_session_name() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -30,7 +40,9 @@ fn properly_convert_layout_with_session_name_and_attach_false() -> Result<(), St let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml", env!("CARGO_MANIFEST_DIR"))); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -38,10 +50,15 @@ fn properly_convert_layout_with_session_name_and_attach_false() -> Result<(), St #[test] fn properly_convert_layout_with_config() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -52,7 +69,9 @@ fn properly_convert_layout_with_config_and_session_name() -> Result<(), String> let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml", env!("CARGO_MANIFEST_DIR"))); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -60,10 +79,15 @@ fn properly_convert_layout_with_config_and_session_name() -> Result<(), String> #[test] fn properly_convert_layout_example_1() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -71,10 +95,15 @@ fn properly_convert_layout_example_1() -> Result<(), String> { #[test] fn properly_convert_layout_example_2() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -82,10 +111,15 @@ fn properly_convert_layout_example_2() -> Result<(), String> { #[test] fn properly_convert_layout_example_3() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/run_htop_layout.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/run_htop_layout.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) @@ -93,10 +127,15 @@ fn properly_convert_layout_example_3() -> Result<(), String> { #[test] fn properly_convert_layout_example_4() -> Result<(), String> { - let fixture = PathBuf::from(format!("{}/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml", env!("CARGO_MANIFEST_DIR"))); + let fixture = PathBuf::from(format!( + "{}/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml", + env!("CARGO_MANIFEST_DIR") + )); let mut handle = File::open(&fixture).map_err(|e| format!("{}", e))?; let mut raw_config_file = String::new(); - handle.read_to_string(&mut raw_config_file).map_err(|e| format!("{}", e))?; + handle + .read_to_string(&mut raw_config_file) + .map_err(|e| format!("{}", e))?; let kdl_config = layout_yaml_to_layout_kdl(&raw_config_file)?; assert_snapshot!(format!("{}", kdl_config)); Ok(()) diff --git a/zellij-client/src/sessions.rs b/zellij-client/src/sessions.rs deleted file mode 100644 index 6a3c07997e..0000000000 --- a/zellij-client/src/sessions.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::process; - -use zellij_utils::consts::ZELLIJ_SOCK_DIR; -use zellij_utils::interprocess::local_socket::LocalSocketStream; -use zellij_utils::ipc::{ClientToServerMsg, IpcSenderWithContext}; - -pub(crate) fn kill_session(name: &str) { - let path = &*ZELLIJ_SOCK_DIR.join(name); - match LocalSocketStream::connect(path) { - Ok(stream) => { - let _ = IpcSenderWithContext::new(stream).send(ClientToServerMsg::KillSession); - }, - Err(e) => { - eprintln!("Error occurred: {:?}", e); - process::exit(1); - }, - }; -} diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 212881e7aa..cd15fdff3f 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -352,7 +352,10 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { .as_ref() .unwrap() .senders - .send_to_pty(PtyInstruction::GoToTab((focused_tab_index + 1) as u32, client_id)) + .send_to_pty(PtyInstruction::GoToTab( + (focused_tab_index + 1) as u32, + client_id, + )) .unwrap(); } } else { diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs index 728ee9b63a..8bd62a2193 100644 --- a/zellij-server/src/os_input_output.rs +++ b/zellij-server/src/os_input_output.rs @@ -198,7 +198,6 @@ pub fn spawn_terminal( quit_cb: Box, default_editor: Option, ) -> Result<(RawFd, RawFd), &'static str> { - log::info!("terminal_action: {:?}", terminal_action); let mut failover_cmd_args = None; let cmd = match terminal_action { TerminalAction::OpenFile(file_to_open, line_number) => { diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index ce2336ba35..81911e1284 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -224,7 +224,7 @@ impl TiledPanes { }, SplitDirection::Vertical => { pane_grid.layout(direction, (*self.display_area.borrow()).rows) - } + }, }; if let Err(e) = &result { log::error!("{:?} relayout of the tab failed: {}", direction, e); @@ -298,7 +298,9 @@ impl TiledPanes { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); let active_pane = self.panes.get_mut(active_pane_id).unwrap(); let full_pane_size = active_pane.position_and_size(); - if let Some((top_winsize, bottom_winsize)) = split(SplitDirection::Horizontal, &full_pane_size) { + if let Some((top_winsize, bottom_winsize)) = + split(SplitDirection::Horizontal, &full_pane_size) + { active_pane.set_geom(top_winsize); new_pane.set_geom(bottom_winsize); self.panes.insert(pid, new_pane); @@ -314,7 +316,9 @@ impl TiledPanes { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); let active_pane = self.panes.get_mut(active_pane_id).unwrap(); let full_pane_size = active_pane.position_and_size(); - if let Some((left_winsize, right_winsize)) = split(SplitDirection::Vertical, &full_pane_size) { + if let Some((left_winsize, right_winsize)) = + split(SplitDirection::Vertical, &full_pane_size) + { active_pane.set_geom(left_winsize); new_pane.set_geom(right_winsize); self.panes.insert(pid, new_pane); @@ -466,7 +470,7 @@ impl TiledPanes { }, Err(e) => { log::error!("Failed to horizontally resize the tab: {:?}", e); - } + }, }; if pane_grid.layout(SplitDirection::Vertical, rows).is_ok() { let row_difference = rows as isize - display_area.rows as isize; diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index 6ed8ddd818..8667e2f6f8 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -178,11 +178,9 @@ impl<'a> PaneResizer<'a> { .filter(|p| { let s = self.get_span(!direction, p.as_ref()); let span_bounds = (s.pos, s.pos + s.size.as_usize()); - bwn(span_bounds.0, boundary) || ( - bwn(boundary.0, span_bounds) && ( - bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1 - ) - ) + bwn(span_bounds.0, boundary) + || (bwn(boundary.0, span_bounds) + && (bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1)) }) .map(|p| self.get_span(direction, p.as_ref())) .collect(); diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs index 7fc407feec..b8efc5ec3b 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -1568,7 +1568,12 @@ impl<'a> TiledPaneGrid<'a> { None } - fn grow_panes(&mut self, panes: &[PaneId], direction: SplitDirection, (width, height): (f64, f64)) { + fn grow_panes( + &mut self, + panes: &[PaneId], + direction: SplitDirection, + (width, height): (f64, f64), + ) { match direction { SplitDirection::Horizontal => { for pane_id in panes { diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index d3e9ccc54a..43fa70112c 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -11,7 +11,7 @@ use zellij_utils::{ errors::{ContextType, PtyContext}, input::{ command::{RunCommand, TerminalAction}, - layout::{PaneLayout, Layout, Run}, + layout::{Layout, PaneLayout, Run}, }, }; @@ -34,7 +34,12 @@ pub(crate) enum PtyInstruction { UpdateActivePane(Option, ClientId), GoToTab(TabIndex, ClientId), // NewTab(Option, Option, ClientId), - NewTab(Option, Option, Option, ClientId), // the String is the tab name + NewTab( + Option, + Option, + Option, + ClientId, + ), // the String is the tab name ClosePane(PaneId), CloseTab(Vec), Exit, @@ -136,21 +141,25 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { .unwrap(); }, PtyInstruction::NewTab(terminal_action, tab_layout, tab_name, client_id) => { -// let tab_name = tab_layout.as_ref().and_then(|layout| { -// if layout.name.is_empty() { -// None -// } else { -// Some(layout.name.clone()) -// } -// }); + // let tab_name = tab_layout.as_ref().and_then(|layout| { + // if layout.name.is_empty() { + // None + // } else { + // Some(layout.name.clone()) + // } + // }); // let merged_layout = layout.template.clone().insert_tab_layout(tab_layout); -// let mut layout = layout.clone(); -// if let Some(tab_layout) = tab_layout { -// layout.insert_tab_layout(&tab_layout).expect("corrupted tab layout"); -// } + // let mut layout = layout.clone(); + // if let Some(tab_layout) = tab_layout { + // layout.insert_tab_layout(&tab_layout).expect("corrupted tab layout"); + // } - pty.spawn_terminals_for_layout(tab_layout.unwrap_or_else(|| layout.new_tab()), terminal_action.clone(), client_id); + pty.spawn_terminals_for_layout( + tab_layout.unwrap_or_else(|| layout.new_tab()), + terminal_action.clone(), + client_id, + ); if let Some(tab_name) = tab_name { // clear current name at first diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 14c4155e6c..11e7e2e616 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -255,7 +255,6 @@ pub(crate) fn route_action( .senders .send_to_screen(ScreenInstruction::ShowFloatingPanes(client_id)) .unwrap(); - }, Some(false) => { session @@ -263,19 +262,28 @@ pub(crate) fn route_action( .send_to_screen(ScreenInstruction::HideFloatingPanes(client_id)) .unwrap(); }, - None => {} + None => {}, }; let open_file = TerminalAction::OpenFile(path_to_file, line_number); let pty_instr = match (split_direction, should_float.unwrap_or(false)) { - (Some(Direction::Left), false) => PtyInstruction::SpawnTerminalVertically(Some(open_file), client_id), - (Some(Direction::Right), false) => PtyInstruction::SpawnTerminalVertically(Some(open_file), client_id), - (Some(Direction::Up), false) => PtyInstruction::SpawnTerminalHorizontally(Some(open_file), client_id), + (Some(Direction::Left), false) => { + PtyInstruction::SpawnTerminalVertically(Some(open_file), client_id) + }, + (Some(Direction::Right), false) => { + PtyInstruction::SpawnTerminalVertically(Some(open_file), client_id) + }, + (Some(Direction::Up), false) => { + PtyInstruction::SpawnTerminalHorizontally(Some(open_file), client_id) + }, (Some(Direction::Down), false) => { PtyInstruction::SpawnTerminalHorizontally(Some(open_file), client_id) }, // No direction specified or should float - defer placement to screen - (None, _) | (_, true) => PtyInstruction::SpawnTerminal(Some(open_file), ClientOrTabIndex::ClientId(client_id)), + (None, _) | (_, true) => PtyInstruction::SpawnTerminal( + Some(open_file), + ClientOrTabIndex::ClientId(client_id), + ), }; session.senders.send_to_pty(pty_instr).unwrap(); }, @@ -291,9 +299,11 @@ pub(crate) fn route_action( .unwrap(); session .senders - .send_to_screen(ScreenInstruction::ChangeModeForAllClients( - get_mode_info(input_mode, attrs, session.capabilities) - )) + .send_to_screen(ScreenInstruction::ChangeModeForAllClients(get_mode_info( + input_mode, + attrs, + session.capabilities, + ))) .unwrap(); }, Action::NewFloatingPane(run_command) => { @@ -301,19 +311,25 @@ pub(crate) fn route_action( .senders .send_to_screen(ScreenInstruction::ShowFloatingPanes(client_id)) .unwrap(); - let run_cmd = run_command.map(|cmd| TerminalAction::RunCommand(cmd.into())).or_else(|| { - session.default_shell.clone() - }); - session.senders.send_to_pty(PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id))).unwrap(); + let run_cmd = run_command + .map(|cmd| TerminalAction::RunCommand(cmd.into())) + .or_else(|| session.default_shell.clone()); + session + .senders + .send_to_pty(PtyInstruction::SpawnTerminal( + run_cmd, + ClientOrTabIndex::ClientId(client_id), + )) + .unwrap(); }, Action::NewTiledPane(direction, run_command) => { session .senders .send_to_screen(ScreenInstruction::HideFloatingPanes(client_id)) .unwrap(); - let run_cmd = run_command.map(|cmd| TerminalAction::RunCommand(cmd.into())).or_else(|| { - session.default_shell.clone() - }); + let run_cmd = run_command + .map(|cmd| TerminalAction::RunCommand(cmd.into())) + .or_else(|| session.default_shell.clone()); let pty_instr = match direction { Some(Direction::Left) => { PtyInstruction::SpawnTerminalVertically(run_cmd, client_id) @@ -393,7 +409,9 @@ pub(crate) fn route_action( let shell = session.default_shell.clone(); session .senders - .send_to_pty(PtyInstruction::NewTab(shell, tab_layout, tab_name, client_id)) + .send_to_pty(PtyInstruction::NewTab( + shell, tab_layout, tab_name, client_id, + )) .unwrap(); }, Action::GoToNextTab => { diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 947e5ed3c6..70831e6a59 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -59,19 +59,19 @@ macro_rules! active_tab_and_connected_client_id { match $screen.get_active_tab_mut($client_id) { Some(active_tab) => { $closure(active_tab, $client_id); - } + }, None => { if let Some(client_id) = $screen.get_first_client_id() { match $screen.get_active_tab_mut(client_id) { Some(active_tab) => { $closure(active_tab, client_id); - } + }, None => { log::error!("Active tab not found for client id: {:?}", $client_id); - } + }, } }; - } + }, } }; } @@ -249,7 +249,9 @@ impl From<&ScreenInstruction> for ScreenContext { }, ScreenInstruction::TerminalColorRegisters(..) => ScreenContext::TerminalColorRegisters, ScreenInstruction::ChangeMode(..) => ScreenContext::ChangeMode, - ScreenInstruction::ChangeModeForAllClients(..) => ScreenContext::ChangeModeForAllClients, + ScreenInstruction::ChangeModeForAllClients(..) => { + ScreenContext::ChangeModeForAllClients + }, ScreenInstruction::ToggleActiveSyncTab(..) => ScreenContext::ToggleActiveSyncTab, ScreenInstruction::ScrollUpAt(..) => ScreenContext::ScrollUpAt, ScreenInstruction::ScrollDownAt(..) => ScreenContext::ScrollDownAt, @@ -945,10 +947,8 @@ impl Screen { for client_id in connected_client_ids { self.change_mode(mode_info.clone(), client_id); if let Some(os_input) = &mut self.bus.os_input { - let _ = os_input.send_to_client( - client_id, - ServerToClientMsg::SwitchToMode(mode_info.mode), - ); + let _ = os_input + .send_to_client(client_id, ServerToClientMsg::SwitchToMode(mode_info.mode)); } } } @@ -1060,8 +1060,11 @@ pub(crate) fn screen_thread_main( ScreenInstruction::NewPane(pid, client_or_tab_index) => { match client_or_tab_index { ClientOrTabIndex::ClientId(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .new_pane(pid, Some(client_id))); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.new_pane(pid, Some(client_id)) + ); }, ClientOrTabIndex::TabIndex(tab_index) => { if let Some(active_tab) = screen.tabs.get_mut(&tab_index) { @@ -1085,102 +1088,157 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_pane_embed_or_floating(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .toggle_pane_embed_or_floating(client_id) + ); screen.unblock_input(); screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins screen.render(); }, ScreenInstruction::ToggleFloatingPanes(client_id, default_shell) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_floating_panes(client_id, default_shell)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .toggle_floating_panes(client_id, default_shell) + ); screen.unblock_input(); screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins screen.render(); }, ScreenInstruction::ShowFloatingPanes(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, _client_id: ClientId| tab - .show_floating_panes()); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, _client_id: ClientId| tab.show_floating_panes() + ); screen.unblock_input(); screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins screen.render(); }, ScreenInstruction::HideFloatingPanes(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, _client_id: ClientId| tab - .hide_floating_panes()); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, _client_id: ClientId| tab.hide_floating_panes() + ); screen.unblock_input(); screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins screen.render(); }, ScreenInstruction::HorizontalSplit(pid, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .horizontal_split(pid, client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.horizontal_split(pid, client_id) + ); screen.unblock_input(); screen.update_tabs(); screen.render(); }, ScreenInstruction::VerticalSplit(pid, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .vertical_split(pid, client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.vertical_split(pid, client_id) + ); screen.unblock_input(); screen.update_tabs(); screen.render(); }, ScreenInstruction::WriteCharacter(bytes, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| { - match tab.is_sync_panes_active() { - true => tab.write_to_terminals_on_current_tab(bytes), - false => tab.write_to_active_terminal(bytes, client_id), + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| { + match tab.is_sync_panes_active() { + true => tab.write_to_terminals_on_current_tab(bytes), + false => tab.write_to_active_terminal(bytes, client_id), + } } - }); + ); }, ScreenInstruction::ResizeLeft(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .resize_left(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.resize_left(client_id) + ); screen.render(); }, ScreenInstruction::ResizeRight(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .resize_right(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.resize_right(client_id) + ); screen.render(); }, ScreenInstruction::ResizeDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .resize_down(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.resize_down(client_id) + ); screen.render(); }, ScreenInstruction::ResizeUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_up(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.resize_up(client_id) + ); screen.render(); }, ScreenInstruction::ResizeIncrease(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .resize_increase(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.resize_increase(client_id) + ); screen.render(); }, ScreenInstruction::ResizeDecrease(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .resize_decrease(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.resize_decrease(client_id) + ); screen.render(); }, ScreenInstruction::SwitchFocus(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .focus_next_pane(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.focus_next_pane(client_id) + ); screen.render(); }, ScreenInstruction::FocusNextPane(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .focus_next_pane(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.focus_next_pane(client_id) + ); screen.render(); }, ScreenInstruction::FocusPreviousPane(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .focus_previous_pane(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.focus_previous_pane(client_id) + ); screen.render(); }, ScreenInstruction::MoveFocusLeft(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_focus_left(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_focus_left(client_id) + ); screen.render(); }, ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id) => { @@ -1189,13 +1247,19 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::MoveFocusDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_focus_down(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_focus_down(client_id) + ); screen.render(); }, ScreenInstruction::MoveFocusRight(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_focus_right(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_focus_right(client_id) + ); screen.render(); }, ScreenInstruction::MoveFocusRightOrNextTab(client_id) => { @@ -1204,98 +1268,164 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::MoveFocusUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_focus_up(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_focus_up(client_id) + ); screen.render(); }, ScreenInstruction::DumpScreen(file, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .dump_active_terminal_screen(Some(file.to_string()), client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .dump_active_terminal_screen(Some(file.to_string()), client_id) + ); screen.render(); }, ScreenInstruction::EditScrollback(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .edit_scrollback(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.edit_scrollback(client_id) + ); screen.render(); }, ScreenInstruction::ScrollUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_up(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.scroll_active_terminal_up(client_id) + ); screen.render(); }, ScreenInstruction::MovePane(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_active_pane(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_active_pane(client_id) + ); screen.render(); }, ScreenInstruction::MovePaneDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_active_pane_down(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_down(client_id) + ); screen.render(); }, ScreenInstruction::MovePaneUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_active_pane_up(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_up(client_id) + ); screen.render(); }, ScreenInstruction::MovePaneRight(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_active_pane_right(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_right(client_id) + ); screen.render(); }, ScreenInstruction::MovePaneLeft(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .move_active_pane_left(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_left(client_id) + ); screen.render(); }, ScreenInstruction::ScrollUpAt(point, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .handle_scrollwheel_up(&point, 3, client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .handle_scrollwheel_up(&point, 3, client_id) + ); screen.render(); }, ScreenInstruction::ScrollDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_down(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.scroll_active_terminal_down(client_id) + ); screen.render(); }, ScreenInstruction::ScrollDownAt(point, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .handle_scrollwheel_down(&point, 3, client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .handle_scrollwheel_down(&point, 3, client_id) + ); screen.render(); }, ScreenInstruction::ScrollToBottom(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_to_bottom(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .scroll_active_terminal_to_bottom(client_id) + ); screen.render(); }, ScreenInstruction::PageScrollUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_up_page(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .scroll_active_terminal_up_page(client_id) + ); screen.render(); }, ScreenInstruction::PageScrollDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_down_page(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .scroll_active_terminal_down_page(client_id) + ); screen.render(); }, ScreenInstruction::HalfPageScrollUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_up_half_page(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .scroll_active_terminal_up_half_page(client_id) + ); screen.render(); }, ScreenInstruction::HalfPageScrollDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .scroll_active_terminal_down_half_page(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .scroll_active_terminal_down_half_page(client_id) + ); screen.render(); }, ScreenInstruction::ClearScroll(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .clear_active_terminal_scroll(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .clear_active_terminal_scroll(client_id) + ); screen.render(); }, ScreenInstruction::CloseFocusedPane(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .close_focused_pane(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.close_focused_pane(client_id) + ); screen.update_tabs(); screen.render(); }, @@ -1330,18 +1460,28 @@ pub(crate) fn screen_thread_main( screen.update_tabs(); }, ScreenInstruction::UpdatePaneName(c, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .update_active_pane_name(c, client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.update_active_pane_name(c, client_id) + ); screen.render(); }, ScreenInstruction::UndoRenamePane(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .undo_active_rename_pane(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.undo_active_rename_pane(client_id) + ); screen.render(); }, ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_active_pane_fullscreen(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .toggle_active_pane_fullscreen(client_id) + ); screen.update_tabs(); screen.render(); }, @@ -1419,8 +1559,11 @@ pub(crate) fn screen_thread_main( screen.render(); }, ScreenInstruction::ToggleActiveSyncTab(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, _client_id: ClientId| tab - .toggle_sync_panes_is_active()); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, _client_id: ClientId| tab.toggle_sync_panes_is_active() + ); screen.update_tabs(); screen.render(); }, @@ -1521,32 +1664,52 @@ pub(crate) fn screen_thread_main( screen.unblock_input(); }, ScreenInstruction::UpdateSearch(c, client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .update_search_term(c, client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.update_search_term(c, client_id) + ); screen.render(); }, ScreenInstruction::SearchDown(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .search_down(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.search_down(client_id) + ); screen.render(); }, ScreenInstruction::SearchUp(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab.search_up(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.search_up(client_id) + ); screen.render(); }, ScreenInstruction::SearchToggleCaseSensitivity(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_search_case_sensitivity(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab + .toggle_search_case_sensitivity(client_id) + ); screen.render(); }, ScreenInstruction::SearchToggleWrap(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_search_wrap(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.toggle_search_wrap(client_id) + ); screen.render(); }, ScreenInstruction::SearchToggleWholeWord(client_id) => { - active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_search_whole_words(client_id)); + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.toggle_search_whole_words(client_id) + ); screen.render(); }, } diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 06149ca536..c2e6066eb2 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -147,12 +147,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout( - PaneLayout::default(), - vec![1], - index, - client_id, - ); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); tab } @@ -201,13 +196,13 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) terminal_emulator_colors, terminal_emulator_color_codes, ); - let pane_ids = tab_layout.extract_run_instructions().iter().enumerate().map(|(i, _)| i as i32).collect(); - tab.apply_layout( - tab_layout, - pane_ids, - index, - client_id, - ); + let pane_ids = tab_layout + .extract_run_instructions() + .iter() + .enumerate() + .map(|(i, _)| i as i32) + .collect(); + tab.apply_layout(tab_layout, pane_ids, index, client_id); tab } @@ -319,12 +314,7 @@ fn create_new_tab_with_sixel_support( terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout( - PaneLayout::default(), - vec![1], - index, - client_id, - ); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); tab } diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index eb77ad58d4..3c5da69df1 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -131,12 +131,7 @@ fn create_new_tab(size: Size) -> Tab { terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout( - PaneLayout::default(), - vec![1], - index, - client_id, - ); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); tab } @@ -182,12 +177,7 @@ fn create_new_tab_with_cell_size( terminal_emulator_colors, terminal_emulator_color_codes, ); - tab.apply_layout( - PaneLayout::default(), - vec![1], - index, - client_id, - ); + tab.apply_layout(PaneLayout::default(), vec![1], index, client_id); tab } diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index f5d85aa62c..d1f10c53df 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1,51 +1,52 @@ -use super::{CopyOptions, Screen, ScreenInstruction, screen_thread_main}; +use super::{screen_thread_main, CopyOptions, Screen, ScreenInstruction}; use crate::panes::PaneId; -use insta::assert_snapshot; use crate::{ - route::route_action, + channels::SenderWithContext, os_input_output::{AsyncReader, Pid, ServerOsApi}, + route::route_action, thread_bus::Bus, - channels::SenderWithContext, - ClientId, - ServerInstruction, SessionMetaData, ThreadSenders, + ClientId, ServerInstruction, SessionMetaData, ThreadSenders, }; +use insta::assert_snapshot; use std::path::PathBuf; -use zellij_utils::input::options::Options; -use zellij_utils::input::actions::{Action, ResizeDirection, Direction}; +use zellij_utils::cli::CliAction; +use zellij_utils::errors::ErrorContext; +use zellij_utils::input::actions::{Action, Direction, ResizeDirection}; use zellij_utils::input::command::TerminalAction; use zellij_utils::input::layout::{PaneLayout, SplitDirection}; -use zellij_utils::cli::CliAction; +use zellij_utils::input::options::Options; use zellij_utils::ipc::IpcReceiverWithContext; -use zellij_utils::errors::ErrorContext; use zellij_utils::pane_size::{Size, SizeInPixels}; use crate::pty_writer::PtyWriteInstruction; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; +use crate::{pty::PtyInstruction, wasm_vm::PluginInstruction}; use zellij_utils::ipc::PixelDimensions; use zellij_utils::nix; -use crate::{ - pty::PtyInstruction, - wasm_vm::PluginInstruction, -}; use zellij_utils::{ channels::{self, ChannelWithContext, Receiver}, - data::{ModeInfo, Palette, PluginCapabilities, InputMode}, + data::{InputMode, ModeInfo, Palette, PluginCapabilities}, interprocess::local_socket::LocalSocketStream, - ipc::{ClientToServerMsg, ServerToClientMsg, ClientAttributes}, + ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg}, }; +use crate::panes::grid::Grid; +use crate::panes::link_handler::LinkHandler; +use crate::panes::sixel::SixelImageStore; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use crate::panes::sixel::SixelImageStore; -use crate::panes::grid::Grid; -use crate::panes::link_handler::LinkHandler; use zellij_utils::vte; // TODO: deduplicate with identical function in tab_integration_tests -fn take_snapshot_and_cursor_coordinates(ansi_instructions: &str, rows: usize, columns: usize, palette: Palette) -> (Option<(usize, usize)>, String) { +fn take_snapshot_and_cursor_coordinates( + ansi_instructions: &str, + rows: usize, + columns: usize, + palette: Palette, +) -> (Option<(usize, usize)>, String) { let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let character_cell_size = Rc::new(RefCell::new(Some(SizeInPixels { @@ -68,36 +69,52 @@ fn take_snapshot_and_cursor_coordinates(ansi_instructions: &str, rows: usize, co (grid.cursor_coordinates(), format!("{:?}", grid)) } -fn take_snapshots_and_cursor_coordinates_from_render_events<'a>(all_events: impl Iterator, screen_size: Size) -> Vec<(Option<(usize, usize)>, String)> { - let snapshots: Vec<(Option<(usize, usize)>, String)> = all_events.filter_map(|server_instruction| { - match server_instruction { - ServerInstruction::Render(output) => { - if let Some(output) = output { - // note this only takes a snapshot of the first client! - let raw_snapshot = output.get(&1).unwrap(); - let snapshot = take_snapshot_and_cursor_coordinates( - raw_snapshot, - screen_size.rows, - screen_size.cols, - Palette::default(), - ); - Some(snapshot) - } else { - None - } - }, - _ => None - } - }).collect(); +fn take_snapshots_and_cursor_coordinates_from_render_events<'a>( + all_events: impl Iterator, + screen_size: Size, +) -> Vec<(Option<(usize, usize)>, String)> { + let snapshots: Vec<(Option<(usize, usize)>, String)> = all_events + .filter_map(|server_instruction| { + match server_instruction { + ServerInstruction::Render(output) => { + if let Some(output) = output { + // note this only takes a snapshot of the first client! + let raw_snapshot = output.get(&1).unwrap(); + let snapshot = take_snapshot_and_cursor_coordinates( + raw_snapshot, + screen_size.rows, + screen_size.cols, + Palette::default(), + ); + Some(snapshot) + } else { + None + } + }, + _ => None, + } + }) + .collect(); snapshots } -fn send_cli_action_to_server(session_metadata: &SessionMetaData, cli_action: CliAction, mock_screen: &mut MockScreen, client_id: ClientId) { +fn send_cli_action_to_server( + session_metadata: &SessionMetaData, + cli_action: CliAction, + mock_screen: &mut MockScreen, + client_id: ClientId, +) { let os_input = Box::new(mock_screen.os_input.clone()); let to_server = mock_screen.to_server.clone(); let actions = Action::actions_from_cli(cli_action).unwrap(); for action in actions { - route_action(action, &session_metadata, &*os_input, &to_server.clone(), client_id); + route_action( + action, + &session_metadata, + &*os_input, + &to_server.clone(), + client_id, + ); } } @@ -145,7 +162,12 @@ impl ServerOsApi for FakeInputOutput { client_id: ClientId, msg: ServerToClientMsg, ) -> Result<(), &'static str> { - self.server_to_client_messages.lock().unwrap().entry(client_id).or_insert_with(Vec::new).push(msg); + self.server_to_client_messages + .lock() + .unwrap() + .entry(client_id) + .or_insert_with(Vec::new) + .push(msg); Ok(()) } fn new_client( @@ -166,7 +188,10 @@ impl ServerOsApi for FakeInputOutput { } fn write_to_file(&mut self, contents: String, filename: Option) { if let Some(filename) = filename { - self.fake_filesystem.lock().unwrap().insert(filename, contents); + self.fake_filesystem + .lock() + .unwrap() + .insert(filename, contents); } } } @@ -226,10 +251,18 @@ impl MockScreen { Some(&self.to_server.clone()), Some(&self.to_pty_writer.clone()), Some(Box::new(self.os_input.clone())), - ).should_silently_fail(); + ) + .should_silently_fail(); let screen_thread = std::thread::Builder::new() .name("screen_thread".to_string()) - .spawn(move || screen_thread_main(screen_bus, None, client_attributes, Box::new(config_options))) + .spawn(move || { + screen_thread_main( + screen_bus, + None, + client_attributes, + Box::new(config_options), + ) + }) .unwrap(); let pane_layout = initial_layout.unwrap_or_default(); let pane_count = pane_layout.extract_run_instructions().len(); @@ -237,7 +270,11 @@ impl MockScreen { for i in 0..pane_count { pane_ids.push(i as i32); } - let _ = self.to_screen.send(ScreenInstruction::NewTab(pane_layout, pane_ids, self.main_client_id)); + let _ = self.to_screen.send(ScreenInstruction::NewTab( + pane_layout, + pane_ids, + self.main_client_id, + )); screen_thread } pub fn new_tab(&mut self, tab_layout: PaneLayout) { @@ -246,7 +283,11 @@ impl MockScreen { for i in 0..pane_count { pane_ids.push(i as i32); } - let _ = self.to_screen.send(ScreenInstruction::NewTab(tab_layout, pane_ids, self.main_client_id)); + let _ = self.to_screen.send(ScreenInstruction::NewTab( + tab_layout, + pane_ids, + self.main_client_id, + )); } pub fn teardown(&mut self, threads: Vec>) { let _ = self.to_pty.send(PtyInstruction::Exit); @@ -271,18 +312,20 @@ impl MockScreen { pty_writer_thread: None, } } - } impl MockScreen { pub fn new(size: Size) -> Self { - let (to_server, server_receiver): ChannelWithContext = channels::bounded(50); + let (to_server, server_receiver): ChannelWithContext = + channels::bounded(50); let to_server = SenderWithContext::new(to_server); - let (to_screen, screen_receiver): ChannelWithContext = channels::unbounded(); + let (to_screen, screen_receiver): ChannelWithContext = + channels::unbounded(); let to_screen = SenderWithContext::new(to_screen); - let (to_plugin, plugin_receiver): ChannelWithContext = channels::unbounded(); + let (to_plugin, plugin_receiver): ChannelWithContext = + channels::unbounded(); let to_plugin = SenderWithContext::new(to_plugin); let (to_pty, pty_receiver): ChannelWithContext = channels::unbounded(); let to_pty = SenderWithContext::new(to_pty); @@ -296,7 +339,7 @@ impl MockScreen { ..Default::default() }; let capabilities = PluginCapabilities { - arrow_fonts: Default::default() + arrow_fonts: Default::default(), }; let session_metadata = SessionMetaData { @@ -346,32 +389,28 @@ macro_rules! log_actions_in_thread { .name("pty_writer_thread".to_string()) .spawn({ let log = $arc_mutex_log.clone(); - move || { - loop { - let (event, _err_ctx) = $receiver.recv().expect("failed to receive event on channel"); - match event { - $exit_event => { - log.lock().unwrap().push(event); - break; - } - _ => { - log.lock().unwrap().push(event); - } - } + move || loop { + let (event, _err_ctx) = $receiver + .recv() + .expect("failed to receive event on channel"); + match event { + $exit_event => { + log.lock().unwrap().push(event); + break; + }, + _ => { + log.lock().unwrap().push(event); + }, } } }) .unwrap() - } + }; } fn new_tab(screen: &mut Screen, pid: i32) { let client_id = 1; - screen.new_tab( - PaneLayout::default(), - vec![pid], - client_id, - ); + screen.new_tab(PaneLayout::default(), vec![pid], client_id); } #[test] @@ -848,7 +887,9 @@ pub fn send_cli_write_chars_action_to_screen() { PtyWriteInstruction::Exit, pty_writer_receiver ); - let cli_action = CliAction::WriteChars { chars: "input from the cli".into() }; + let cli_action = CliAction::WriteChars { + chars: "input from the cli".into(), + }; send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_writer_thread, screen_thread]); @@ -872,7 +913,9 @@ pub fn send_cli_write_action_to_screen() { PtyWriteInstruction::Exit, pty_writer_receiver ); - let cli_action = CliAction::Write { bytes: vec![102, 111, 111] }; + let cli_action = CliAction::Write { + bytes: vec![102, 111, 111], + }; send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_writer_thread, screen_thread]); @@ -881,10 +924,7 @@ pub fn send_cli_write_action_to_screen() { #[test] pub fn send_cli_resize_action_to_screen() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -899,10 +939,20 @@ pub fn send_cli_resize_action_to_screen() { ServerInstruction::KillSession, server_receiver ); - let resize_cli_action = CliAction::Resize { resize_direction: ResizeDirection::Left }; - send_cli_action_to_server(&session_metadata, resize_cli_action, &mut mock_screen, client_id); + let resize_cli_action = CliAction::Resize { + resize_direction: ResizeDirection::Left, + }; + send_cli_action_to_server( + &session_metadata, + resize_cli_action, + &mut mock_screen, + client_id, + ); mock_screen.teardown(vec![pty_writer_thread, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -912,10 +962,7 @@ pub fn send_cli_resize_action_to_screen() { #[test] pub fn send_cli_focus_next_pane_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -931,10 +978,18 @@ pub fn send_cli_focus_next_pane_action() { server_receiver ); let focus_next_pane_action = CliAction::FocusNextPane; - send_cli_action_to_server(&session_metadata, focus_next_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + focus_next_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (cursor_coordinates, _snapshot) in snapshots { // here we assert he cursor_coordinates to let us know if we switched the pane focus @@ -945,10 +1000,7 @@ pub fn send_cli_focus_next_pane_action() { #[test] pub fn send_cli_focus_previous_pane_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -964,10 +1016,18 @@ pub fn send_cli_focus_previous_pane_action() { server_receiver ); let focus_next_pane_action = CliAction::FocusPreviousPane; - send_cli_action_to_server(&session_metadata, focus_next_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + focus_next_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (cursor_coordinates, _snapshot) in snapshots { // here we assert he cursor_coordinates to let us know if we switched the pane focus @@ -978,10 +1038,7 @@ pub fn send_cli_focus_previous_pane_action() { #[test] pub fn send_cli_move_focus_pane_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -996,11 +1053,21 @@ pub fn send_cli_move_focus_pane_action() { ServerInstruction::KillSession, server_receiver ); - let move_focus_action = CliAction::MoveFocus { direction: Direction::Right }; - send_cli_action_to_server(&session_metadata, move_focus_action, &mut mock_screen, client_id); + let move_focus_action = CliAction::MoveFocus { + direction: Direction::Right, + }; + send_cli_action_to_server( + &session_metadata, + move_focus_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (cursor_coordinates, _snapshot) in snapshots { // here we assert he cursor_coordinates to let us know if we switched the pane focus @@ -1011,10 +1078,7 @@ pub fn send_cli_move_focus_pane_action() { #[test] pub fn send_cli_move_focus_or_tab_pane_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1029,11 +1093,21 @@ pub fn send_cli_move_focus_or_tab_pane_action() { ServerInstruction::KillSession, server_receiver ); - let move_focus_action = CliAction::MoveFocusOrTab { direction: Direction::Right }; - send_cli_action_to_server(&session_metadata, move_focus_action, &mut mock_screen, client_id); + let move_focus_action = CliAction::MoveFocusOrTab { + direction: Direction::Right, + }; + send_cli_action_to_server( + &session_metadata, + move_focus_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (cursor_coordinates, _snapshot) in snapshots { // here we assert he cursor_coordinates to let us know if we switched the pane focus @@ -1044,10 +1118,7 @@ pub fn send_cli_move_focus_or_tab_pane_action() { #[test] pub fn send_cli_move_pane_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1062,11 +1133,16 @@ pub fn send_cli_move_pane_action() { ServerInstruction::KillSession, server_receiver ); - let cli_action = CliAction::MovePane { direction: Direction::Right }; + let cli_action = CliAction::MovePane { + direction: Direction::Right, + }; send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1076,10 +1152,7 @@ pub fn send_cli_move_pane_action() { #[test] pub fn send_cli_dump_screen_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1094,20 +1167,25 @@ pub fn send_cli_dump_screen_action() { ServerInstruction::KillSession, server_receiver ); - let cli_action = CliAction::DumpScreen { path: PathBuf::from("/tmp/foo") }; - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, "fill pane up with something".as_bytes().to_vec())); + let cli_action = CliAction::DumpScreen { + path: PathBuf::from("/tmp/foo"), + }; + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + "fill pane up with something".as_bytes().to_vec(), + )); send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - assert_snapshot!(format!("{:?}", *mock_screen.os_input.fake_filesystem.lock().unwrap())); + assert_snapshot!(format!( + "{:?}", + *mock_screen.os_input.fake_filesystem.lock().unwrap() + )); } #[test] pub fn send_cli_edit_scrollback_action() { - let size = Size { - cols: 80, - rows: 20, - }; + let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1123,14 +1201,27 @@ pub fn send_cli_edit_scrollback_action() { pty_receiver ); let cli_action = CliAction::EditScrollback; - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, "fill pane up with something".as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + "fill pane up with something".as_bytes().to_vec(), + )); send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![pty_thread, screen_thread]); - let dumped_file_name = mock_screen.os_input.fake_filesystem.lock().unwrap().keys().next().unwrap().clone(); + let dumped_file_name = mock_screen + .os_input + .fake_filesystem + .lock() + .unwrap() + .keys() + .next() + .unwrap() + .clone(); let mut found_instruction = false; for instruction in received_pty_instructions.lock().unwrap().iter() { - if let PtyInstruction::OpenInPlaceEditor(scrollback_contents_file, terminal_id, client_id) = instruction { + if let PtyInstruction::OpenInPlaceEditor(scrollback_contents_file, terminal_id, client_id) = + instruction + { assert_eq!(scrollback_contents_file, &PathBuf::from(&dumped_file_name)); assert_eq!(terminal_id, &Some(1)); assert_eq!(client_id, &1); @@ -1142,10 +1233,7 @@ pub fn send_cli_edit_scrollback_action() { #[test] pub fn send_cli_scroll_up_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1165,15 +1253,31 @@ pub fn send_cli_scroll_up_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); // we send two actions here because only the last line in the pane is empty, so one action // won't show in a render - send_cli_action_to_server(&session_metadata, cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + cli_action.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1183,10 +1287,7 @@ pub fn send_cli_scroll_up_action() { #[test] pub fn send_cli_scroll_down_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1207,20 +1308,56 @@ pub fn send_cli_scroll_down_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); // scroll up some - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); // scroll down some - send_cli_action_to_server(&session_metadata, scroll_down_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_down_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + scroll_down_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_down_cli_action.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1230,10 +1367,7 @@ pub fn send_cli_scroll_down_action() { #[test] pub fn send_cli_scroll_to_bottom_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1254,19 +1388,50 @@ pub fn send_cli_scroll_to_bottom_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); // scroll up some - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, scroll_up_cli_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + scroll_up_cli_action.clone(), + &mut mock_screen, + client_id, + ); // scroll to bottom - send_cli_action_to_server(&session_metadata, scroll_to_bottom_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + scroll_to_bottom_action.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1276,10 +1441,7 @@ pub fn send_cli_scroll_to_bottom_action() { #[test] pub fn send_cli_page_scroll_up_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1299,12 +1461,23 @@ pub fn send_cli_page_scroll_up_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); - send_cli_action_to_server(&session_metadata, page_scroll_up_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + page_scroll_up_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1314,10 +1487,7 @@ pub fn send_cli_page_scroll_up_action() { #[test] pub fn send_cli_page_scroll_down_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1338,18 +1508,39 @@ pub fn send_cli_page_scroll_down_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); // scroll up some - send_cli_action_to_server(&session_metadata, page_scroll_up_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, page_scroll_up_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + page_scroll_up_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + page_scroll_up_action.clone(), + &mut mock_screen, + client_id, + ); // scroll down - send_cli_action_to_server(&session_metadata, page_scroll_down_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + page_scroll_down_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1359,10 +1550,7 @@ pub fn send_cli_page_scroll_down_action() { #[test] pub fn send_cli_half_page_scroll_up_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1382,12 +1570,23 @@ pub fn send_cli_half_page_scroll_up_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); - send_cli_action_to_server(&session_metadata, half_page_scroll_up_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + half_page_scroll_up_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1397,10 +1596,7 @@ pub fn send_cli_half_page_scroll_up_action() { #[test] pub fn send_cli_half_page_scroll_down_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1421,18 +1617,39 @@ pub fn send_cli_half_page_scroll_down_action() { for i in 0..20 { pane_contents.push_str(&format!("fill pane up with something {}\n\r", i)); } - let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes(0, pane_contents.as_bytes().to_vec())); + let _ = mock_screen.to_screen.send(ScreenInstruction::PtyBytes( + 0, + pane_contents.as_bytes().to_vec(), + )); std::thread::sleep(std::time::Duration::from_millis(100)); // scroll up some - send_cli_action_to_server(&session_metadata, half_page_scroll_up_action.clone(), &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, half_page_scroll_up_action.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + half_page_scroll_up_action.clone(), + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + half_page_scroll_up_action.clone(), + &mut mock_screen, + client_id, + ); // scroll down - send_cli_action_to_server(&session_metadata, half_page_scroll_down_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + half_page_scroll_down_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1442,10 +1659,7 @@ pub fn send_cli_half_page_scroll_down_action() { #[test] pub fn send_cli_toggle_full_screen_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1461,10 +1675,18 @@ pub fn send_cli_toggle_full_screen_action() { server_receiver ); let toggle_full_screen_action = CliAction::ToggleFullscreen; - send_cli_action_to_server(&session_metadata, toggle_full_screen_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_full_screen_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1474,10 +1696,7 @@ pub fn send_cli_toggle_full_screen_action() { #[test] pub fn send_cli_toggle_pane_frames_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1493,10 +1712,18 @@ pub fn send_cli_toggle_pane_frames_action() { server_receiver ); let toggle_pane_frames_action = CliAction::TogglePaneFrames; - send_cli_action_to_server(&session_metadata, toggle_pane_frames_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_pane_frames_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1525,9 +1752,21 @@ pub fn send_cli_toggle_active_tab_sync_action() { pty_writer_receiver ); let cli_toggle_active_tab_sync_action = CliAction::ToggleActiveSyncTab; - let cli_write_action = CliAction::Write { bytes: vec![102, 111, 111] }; - send_cli_action_to_server(&session_metadata, cli_toggle_active_tab_sync_action, &mut mock_screen, client_id); - send_cli_action_to_server(&session_metadata, cli_write_action, &mut mock_screen, client_id); + let cli_write_action = CliAction::Write { + bytes: vec![102, 111, 111], + }; + send_cli_action_to_server( + &session_metadata, + cli_toggle_active_tab_sync_action, + &mut mock_screen, + client_id, + ); + send_cli_action_to_server( + &session_metadata, + cli_write_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_writer_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1558,9 +1797,14 @@ pub fn send_cli_new_pane_action_with_default_parameters() { command: None, cwd: None, args: None, - floating: None + floating: None, }; - send_cli_action_to_server(&session_metadata, cli_new_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_new_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1591,9 +1835,14 @@ pub fn send_cli_new_pane_action_with_split_direction() { command: None, cwd: None, args: None, - floating: None + floating: None, }; - send_cli_action_to_server(&session_metadata, cli_new_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_new_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1624,9 +1873,14 @@ pub fn send_cli_new_pane_action_with_command_cwd_and_args() { command: Some("htop".into()), cwd: Some("/some/folder".into()), args: Some("-h --something arg".into()), - floating: None + floating: None, }; - send_cli_action_to_server(&session_metadata, cli_new_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_new_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1656,9 +1910,14 @@ pub fn send_cli_edit_action_with_default_parameters() { file: PathBuf::from("/file/to/edit"), direction: None, line_number: None, - floating: None + floating: None, }; - send_cli_action_to_server(&session_metadata, cli_edit_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_edit_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1688,9 +1947,14 @@ pub fn send_cli_edit_action_with_line_number() { file: PathBuf::from("/file/to/edit"), direction: None, line_number: Some(100), - floating: None + floating: None, }; - send_cli_action_to_server(&session_metadata, cli_edit_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_edit_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1720,9 +1984,14 @@ pub fn send_cli_edit_action_with_split_direction() { file: PathBuf::from("/file/to/edit"), direction: Some(Direction::Down), line_number: None, - floating: None + floating: None, }; - send_cli_action_to_server(&session_metadata, cli_edit_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + cli_edit_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1741,19 +2010,30 @@ pub fn send_cli_switch_mode_action() { initial_layout.children_split_direction = SplitDirection::Vertical; initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); - let cli_switch_mode = CliAction::SwitchMode { input_mode: InputMode::Locked }; - send_cli_action_to_server(&session_metadata, cli_switch_mode, &mut mock_screen, client_id); + let cli_switch_mode = CliAction::SwitchMode { + input_mode: InputMode::Locked, + }; + send_cli_action_to_server( + &session_metadata, + cli_switch_mode, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be mock_screen.teardown(vec![screen_thread]); - assert_snapshot!(format!("{:?}", *mock_screen.os_input.server_to_client_messages.lock().unwrap())); + assert_snapshot!(format!( + "{:?}", + *mock_screen + .os_input + .server_to_client_messages + .lock() + .unwrap() + )); } #[test] pub fn send_cli_toggle_pane_embed_or_float() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1770,13 +2050,26 @@ pub fn send_cli_toggle_pane_embed_or_float() { ); let toggle_pane_embed_or_floating = CliAction::TogglePaneEmbedOrFloating; // first time to float - send_cli_action_to_server(&session_metadata, toggle_pane_embed_or_floating.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_pane_embed_or_floating.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // second time to embed - send_cli_action_to_server(&session_metadata, toggle_pane_embed_or_floating.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_pane_embed_or_floating.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1786,10 +2079,7 @@ pub fn send_cli_toggle_pane_embed_or_float() { #[test] pub fn send_cli_toggle_floating_panes() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1807,16 +2097,34 @@ pub fn send_cli_toggle_floating_panes() { let toggle_pane_embed_or_floating = CliAction::TogglePaneEmbedOrFloating; let toggle_floating_panes = CliAction::ToggleFloatingPanes; // float the focused pane - send_cli_action_to_server(&session_metadata, toggle_pane_embed_or_floating, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_pane_embed_or_floating, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // toggle floating panes (will hide the floated pane from the previous action) - send_cli_action_to_server(&session_metadata, toggle_floating_panes.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_floating_panes.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // toggle floating panes (will show the floated pane) - send_cli_action_to_server(&session_metadata, toggle_floating_panes.clone(), &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + toggle_floating_panes.clone(), + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1826,10 +2134,7 @@ pub fn send_cli_toggle_floating_panes() { #[test] pub fn send_cli_close_pane_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1845,10 +2150,18 @@ pub fn send_cli_close_pane_action() { server_receiver ); let close_pane_action = CliAction::ClosePane; - send_cli_action_to_server(&session_metadata, close_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + close_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1858,10 +2171,7 @@ pub fn send_cli_close_pane_action() { #[test] pub fn send_cli_rename_pane_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1876,11 +2186,21 @@ pub fn send_cli_rename_pane_action() { ServerInstruction::KillSession, server_receiver ); - let rename_pane_action = CliAction::RenamePane { name: "my_new_pane_title".into() }; - send_cli_action_to_server(&session_metadata, rename_pane_action, &mut mock_screen, client_id); + let rename_pane_action = CliAction::RenamePane { + name: "my_new_pane_title".into(), + }; + send_cli_action_to_server( + &session_metadata, + rename_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1890,10 +2210,7 @@ pub fn send_cli_rename_pane_action() { #[test] pub fn send_cli_undo_rename_pane_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1908,16 +2225,31 @@ pub fn send_cli_undo_rename_pane_action() { ServerInstruction::KillSession, server_receiver ); - let rename_pane_action = CliAction::RenamePane { name: "my_new_pane_title".into() }; + let rename_pane_action = CliAction::RenamePane { + name: "my_new_pane_title".into(), + }; let undo_rename_pane_action = CliAction::UndoRenamePane; // first rename the pane - send_cli_action_to_server(&session_metadata, rename_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + rename_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); // then undo the rename, returning to the default name - send_cli_action_to_server(&session_metadata, undo_rename_pane_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + undo_rename_pane_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -1927,10 +2259,7 @@ pub fn send_cli_undo_rename_pane_action() { #[test] pub fn send_cli_new_tab_action_default_params() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1945,8 +2274,16 @@ pub fn send_cli_new_tab_action_default_params() { PtyInstruction::Exit, pty_receiver ); - let new_tab_action = CliAction::NewTab { name: None, layout: None }; - send_cli_action_to_server(&session_metadata, new_tab_action, &mut mock_screen, client_id); + let new_tab_action = CliAction::NewTab { + name: None, + layout: None, + }; + send_cli_action_to_server( + &session_metadata, + new_tab_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![pty_thread, screen_thread]); assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap())); @@ -1954,10 +2291,7 @@ pub fn send_cli_new_tab_action_default_params() { #[test] pub fn send_cli_new_tab_action_with_name_and_layout() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -1974,27 +2308,38 @@ pub fn send_cli_new_tab_action_with_name_and_layout() { ); let new_tab_action = CliAction::NewTab { name: Some("my-awesome-tab-name".into()), - layout: Some(PathBuf::from(format!("{}/src/unit/fixtures/layout-with-three-panes.kdl", env!("CARGO_MANIFEST_DIR")))), + layout: Some(PathBuf::from(format!( + "{}/src/unit/fixtures/layout-with-three-panes.kdl", + env!("CARGO_MANIFEST_DIR") + ))), }; - send_cli_action_to_server(&session_metadata, new_tab_action, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + new_tab_action, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![pty_thread, screen_thread]); - let new_tab_instruction = received_pty_instructions.lock().unwrap().iter().find(|i| { - if let PtyInstruction::NewTab(..) = i { - return true - } else { - return false - } - }).unwrap().clone(); + let new_tab_instruction = received_pty_instructions + .lock() + .unwrap() + .iter() + .find(|i| { + if let PtyInstruction::NewTab(..) = i { + return true; + } else { + return false; + } + }) + .unwrap() + .clone(); assert_snapshot!(format!("{:#?}", new_tab_instruction)); } #[test] pub fn send_cli_next_tab_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2014,10 +2359,18 @@ pub fn send_cli_next_tab_action() { server_receiver ); let goto_next_tab = CliAction::GoToNextTab; - send_cli_action_to_server(&session_metadata, goto_next_tab, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + goto_next_tab, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -2027,10 +2380,7 @@ pub fn send_cli_next_tab_action() { #[test] pub fn send_cli_previous_tab_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2050,10 +2400,18 @@ pub fn send_cli_previous_tab_action() { server_receiver ); let goto_previous_tab = CliAction::GoToPreviousTab; - send_cli_action_to_server(&session_metadata, goto_previous_tab, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + goto_previous_tab, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -2063,10 +2421,7 @@ pub fn send_cli_previous_tab_action() { #[test] pub fn send_cli_goto_tab_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2089,7 +2444,10 @@ pub fn send_cli_goto_tab_action() { send_cli_action_to_server(&session_metadata, goto_tab, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -2099,10 +2457,7 @@ pub fn send_cli_goto_tab_action() { #[test] pub fn send_cli_close_tab_action() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2125,7 +2480,10 @@ pub fn send_cli_close_tab_action() { send_cli_action_to_server(&session_metadata, close_tab, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(received_server_instructions.lock().unwrap().iter(), size); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); let snapshot_count = snapshots.len(); for (_cursor_coordinates, snapshot) in snapshots { assert_snapshot!(format!("{}", snapshot)); @@ -2135,10 +2493,7 @@ pub fn send_cli_close_tab_action() { #[test] pub fn send_cli_rename_tab() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2157,19 +2512,21 @@ pub fn send_cli_rename_tab() { PluginInstruction::Exit, plugin_receiver ); - let rename_tab = CliAction::RenameTab { name: "new-tab-name".into() }; + let rename_tab = CliAction::RenameTab { + name: "new-tab-name".into(), + }; send_cli_action_to_server(&session_metadata, rename_tab, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![plugin_thread, screen_thread]); - assert_snapshot!(format!("{:#?}", *received_plugin_instructions.lock().unwrap())) + assert_snapshot!(format!( + "{:#?}", + *received_plugin_instructions.lock().unwrap() + )) } #[test] pub fn send_cli_undo_rename_tab() { - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2188,16 +2545,26 @@ pub fn send_cli_undo_rename_tab() { PluginInstruction::Exit, plugin_receiver ); - let rename_tab = CliAction::RenameTab { name: "new-tab-name".into() }; + let rename_tab = CliAction::RenameTab { + name: "new-tab-name".into(), + }; let undo_rename_tab = CliAction::UndoRenameTab; // first rename the tab send_cli_action_to_server(&session_metadata, rename_tab, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // then undo the tab rename to go back to the default name - send_cli_action_to_server(&session_metadata, undo_rename_tab, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + undo_rename_tab, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![plugin_thread, screen_thread]); - assert_snapshot!(format!("{:#?}", *received_plugin_instructions.lock().unwrap())) + assert_snapshot!(format!( + "{:#?}", + *received_plugin_instructions.lock().unwrap() + )) } #[test] @@ -2207,10 +2574,7 @@ pub fn send_cli_update_search_term() { // snapshot styling (we usually strip them) // // if this test fails, it means something it wrong with searching through the CLI - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2229,11 +2593,21 @@ pub fn send_cli_update_search_term() { ServerInstruction::KillSession, server_receiver ); - let update_search_term = CliAction::SearchInput { input: "my-search-term".into() }; - send_cli_action_to_server(&session_metadata, update_search_term, &mut mock_screen, client_id); + let update_search_term = CliAction::SearchInput { + input: "my-search-term".into(), + }; + send_cli_action_to_server( + &session_metadata, + update_search_term, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - assert_snapshot!(format!("{:?}", *received_server_instructions.lock().unwrap())); + assert_snapshot!(format!( + "{:?}", + *received_server_instructions.lock().unwrap() + )); } #[test] @@ -2244,10 +2618,7 @@ pub fn send_cli_go_to_next_and_previous_search_occurrence() { // // if this test fails, it means something it wrong with searching through the CLI // specifically going to the previous/next search occurrence - let size = Size { - cols: 80, - rows: 10, - }; + let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state let mut initial_layout = PaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; @@ -2266,15 +2637,35 @@ pub fn send_cli_go_to_next_and_previous_search_occurrence() { ServerInstruction::KillSession, server_receiver ); - let update_search_term = CliAction::SearchInput { input: "my-search-term".into() }; + let update_search_term = CliAction::SearchInput { + input: "my-search-term".into(), + }; let go_to_previous_search = CliAction::SearchPrevious; let go_to_next_search = CliAction::SearchNext; - send_cli_action_to_server(&session_metadata, update_search_term, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + update_search_term, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); - send_cli_action_to_server(&session_metadata, go_to_previous_search, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + go_to_previous_search, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); - send_cli_action_to_server(&session_metadata, go_to_next_search, &mut mock_screen, client_id); + send_cli_action_to_server( + &session_metadata, + go_to_next_search, + &mut mock_screen, + client_id, + ); std::thread::sleep(std::time::Duration::from_millis(100)); mock_screen.teardown(vec![server_thread, screen_thread]); - assert_snapshot!(format!("{:?}", *received_server_instructions.lock().unwrap())); + assert_snapshot!(format!( + "{:?}", + *received_server_instructions.lock().unwrap() + )); } diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 17c74604c5..c9e8cbbea9 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -1,10 +1,10 @@ +use crate::data::InputMode; use crate::setup::Setup; use crate::{ consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV}, + input::actions::{Direction, ResizeDirection}, input::options::CliOptions, - input::actions::{Action, ResizeDirection, Direction}, }; -use crate::data::InputMode; use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -137,17 +137,25 @@ pub enum Sessions { #[clap(short, long, value_parser, default_missing_value("true"))] floating: Option, }, + /// Edit file with default $EDITOR / $VISUAL in a specific session + #[clap(visible_alias = "e")] + Edit { + file: PathBuf, + #[clap(short, long, value_parser)] + line_number: Option, + #[clap(short, long, value_parser, conflicts_with("floating"))] + direction: Option, + #[clap(short, long, value_parser, default_missing_value("true"))] + floating: Option, + }, ConvertConfig { old_config_file: PathBuf, - output: Option, }, ConvertLayout { old_layout_file: PathBuf, - output: Option, }, ConvertTheme { old_theme_file: PathBuf, - output: Option, }, } @@ -256,6 +264,6 @@ pub enum CliAction { #[clap(short, long, value_parser)] layout: Option, #[clap(short, long, value_parser)] - name: Option + name: Option, }, } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 610e667b73..605cf89808 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -1,5 +1,5 @@ -use crate::input::config::ConversionError; use crate::input::actions::Action; +use crate::input::config::ConversionError; use clap::ArgEnum; use serde::{Deserialize, Serialize}; use std::fmt; @@ -65,7 +65,6 @@ impl FromStr for Key { let mut modifier: Option<&str> = None; let mut main_key: Option<&str> = None; for (index, part) in key_str.split_ascii_whitespace().enumerate() { - // TODO: handle F(u8) if index == 0 && (part == "Ctrl" || part == "Alt") { modifier = Some(part); } else if main_key.is_none() { @@ -101,50 +100,50 @@ impl FromStr for Key { } else { Err(format!("Failed to parse key: {}", key_str).into()) } - } + }, } }, - (None, Some(main_key)) => { - match main_key { - "Backspace" => Ok(Key::Backspace), - "Left" => Ok(Key::Left), - "Right" => Ok(Key::Right), - "Up" => Ok(Key::Up), - "Down" => Ok(Key::Down), - "Home" => Ok(Key::Home), - "End" => Ok(Key::End), - "PageUp" => Ok(Key::PageUp), - "PageDown" => Ok(Key::PageDown), - "Tab" => Ok(Key::BackTab), - "Delete" => Ok(Key::Delete), - "Insert" => Ok(Key::Insert), - "Space" => Ok(Key::Char(' ')), - "Enter" => Ok(Key::Char('\n')), - "Esc" => Ok(Key::Esc), - _ => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Char(key_char)) - } else if key_count > 1 { - if let Some(first_char) = key_chars.next() { - if first_char == 'F' { - let f_index: String = key_chars.collect(); - let f_index: u8 = f_index.parse().map_err(|e| format!("Failed to parse F index: {}", e))?; - if f_index >= 1 && f_index <= 12 { - return Ok(Key::F(f_index)); - } + (None, Some(main_key)) => match main_key { + "Backspace" => Ok(Key::Backspace), + "Left" => Ok(Key::Left), + "Right" => Ok(Key::Right), + "Up" => Ok(Key::Up), + "Down" => Ok(Key::Down), + "Home" => Ok(Key::Home), + "End" => Ok(Key::End), + "PageUp" => Ok(Key::PageUp), + "PageDown" => Ok(Key::PageDown), + "Tab" => Ok(Key::BackTab), + "Delete" => Ok(Key::Delete), + "Insert" => Ok(Key::Insert), + "Space" => Ok(Key::Char(' ')), + "Enter" => Ok(Key::Char('\n')), + "Esc" => Ok(Key::Esc), + _ => { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(Key::Char(key_char)) + } else if key_count > 1 { + if let Some(first_char) = key_chars.next() { + if first_char == 'F' { + let f_index: String = key_chars.collect(); + let f_index: u8 = f_index + .parse() + .map_err(|e| format!("Failed to parse F index: {}", e))?; + if f_index >= 1 && f_index <= 12 { + return Ok(Key::F(f_index)); } } - Err(format!("Failed to parse key: {}", key_str).into()) - } else { - Err(format!("Failed to parse key: {}", key_str).into()) } + Err(format!("Failed to parse key: {}", key_str).into()) + } else { + Err(format!("Failed to parse key: {}", key_str).into()) } - } - } - _ => Err(format!("Failed to parse key: {}", key_str).into()) + }, + }, + _ => Err(format!("Failed to parse key: {}", key_str).into()), } } } @@ -245,7 +244,20 @@ pub enum Event { } /// Describes the different input modes, which change the way that keystrokes will be interpreted. -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize, ArgEnum, PartialOrd, Ord)] +#[derive( + Debug, + PartialEq, + Eq, + Hash, + Copy, + Clone, + EnumIter, + Serialize, + Deserialize, + ArgEnum, + PartialOrd, + Ord, +)] pub enum InputMode { /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading /// to other modes diff --git a/zellij-utils/src/envs.rs b/zellij-utils/src/envs.rs index 1cd203efe0..3596e94e19 100644 --- a/zellij-utils/src/envs.rs +++ b/zellij-utils/src/envs.rs @@ -2,7 +2,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::{ - collections::{HashMap, BTreeMap}, + collections::{BTreeMap, HashMap}, env::{set_var, var}, }; @@ -59,9 +59,7 @@ impl EnvironmentVariables { env } pub fn from_data(data: HashMap) -> Self { - EnvironmentVariables { - env: data - } + EnvironmentVariables { env: data } } /// Set all the ENVIRONMENT VARIABLES, that are configured /// in the configuration and layout files diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 6d85dadd5d..d44809110b 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -4,13 +4,13 @@ use super::command::RunCommandAction; use super::layout::{Layout, PaneLayout}; use crate::cli::CliAction; use crate::data::InputMode; -use crate::input::options::OnForceClose; use crate::input::config::{ConfigError, KdlError}; +use crate::input::options::OnForceClose; use miette::{NamedSource, Report}; use serde::{Deserialize, Serialize}; -use std::str::FromStr; use std::path::PathBuf; +use std::str::FromStr; use crate::position::Position; @@ -30,7 +30,10 @@ impl FromStr for Direction { "Right" | "right" => Ok(Direction::Right), "Up" | "up" => Ok(Direction::Up), "Down" | "down" => Ok(Direction::Down), - _ => Err(format!("Failed to parse Direction. Unknown Direction: {}", s)), + _ => Err(format!( + "Failed to parse Direction. Unknown Direction: {}", + s + )), } } } @@ -54,7 +57,10 @@ impl FromStr for ResizeDirection { "Down" | "down" => Ok(ResizeDirection::Down), "Increase" | "increase" | "+" => Ok(ResizeDirection::Increase), "Decrease" | "decrease" | "-" => Ok(ResizeDirection::Decrease), - _ => Err(format!("Failed to parse ResizeDirection. Unknown ResizeDirection: {}", s)), + _ => Err(format!( + "Failed to parse ResizeDirection. Unknown ResizeDirection: {}", + s + )), } } } @@ -71,7 +77,10 @@ impl FromStr for SearchDirection { match s { "Down" | "down" => Ok(SearchDirection::Down), "Up" | "up" => Ok(SearchDirection::Up), - _ => Err(format!("Failed to parse SearchDirection. Unknown SearchDirection: {}", s)), + _ => Err(format!( + "Failed to parse SearchDirection. Unknown SearchDirection: {}", + s + )), } } } @@ -87,16 +96,22 @@ impl FromStr for SearchOption { type Err = String; fn from_str(s: &str) -> Result { match s { - "CaseSensitivity" | "casesensitivity" | "Casesensitivity" => Ok(SearchOption::CaseSensitivity), + "CaseSensitivity" | "casesensitivity" | "Casesensitivity" => { + Ok(SearchOption::CaseSensitivity) + }, "WholeWord" | "wholeword" | "Wholeword" => Ok(SearchOption::WholeWord), "Wrap" | "wrap" => Ok(SearchOption::Wrap), - _ => Err(format!("Failed to parse SearchOption. Unknown SearchOption: {}", s)), + _ => Err(format!( + "Failed to parse SearchOption. Unknown SearchOption: {}", + s + )), } } } fn split_escaped_whitespace(s: &str) -> Vec { - s.split_ascii_whitespace().map(|s| String::from(s)) + s.split_ascii_whitespace() + .map(|s| String::from(s)) .fold(vec![], |mut acc, part| { if let Some(previous_part) = acc.last_mut() { if previous_part.ends_with('\\') { @@ -255,7 +270,9 @@ impl Action { CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus(direction)]), CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]), CliAction::MovePane { direction } => Ok(vec![Action::MovePane(Some(direction))]), - CliAction::DumpScreen { path } => Ok(vec![Action::DumpScreen(path.as_os_str().to_string_lossy().into())]), + CliAction::DumpScreen { path } => Ok(vec![Action::DumpScreen( + path.as_os_str().to_string_lossy().into(), + )]), CliAction::EditScrollback => Ok(vec![Action::EditScrollback]), CliAction::ScrollUp => Ok(vec![Action::ScrollUp]), CliAction::ScrollDown => Ok(vec![Action::ScrollDown]), @@ -267,30 +284,54 @@ impl Action { CliAction::ToggleFullscreen => Ok(vec![Action::ToggleFocusFullscreen]), CliAction::TogglePaneFrames => Ok(vec![Action::TogglePaneFrames]), CliAction::ToggleActiveSyncTab => Ok(vec![Action::ToggleActiveSyncTab]), - CliAction::NewPane { direction, command, cwd, args, floating } => { - match command { - Some(command) => { - let (command, args) = split_command_and_args(command, args); - let run_command_action = RunCommandAction { command, args, cwd, direction }; - match floating { - Some(true) => Ok(vec![Action::NewFloatingPane(Some(run_command_action))]), - _ => Ok(vec![Action::NewTiledPane(direction, Some(run_command_action))]), - } - }, - None => { - match floating { - Some(true) => Ok(vec![Action::NewFloatingPane(None)]), - _ => Ok(vec![Action::NewTiledPane(direction, None)]), - } + CliAction::NewPane { + direction, + command, + cwd, + args, + floating, + } => match command { + Some(command) => { + let (command, args) = split_command_and_args(command, args); + let run_command_action = RunCommandAction { + command, + args, + cwd, + direction, + }; + match floating { + Some(true) => Ok(vec![Action::NewFloatingPane(Some(run_command_action))]), + _ => Ok(vec![Action::NewTiledPane( + direction, + Some(run_command_action), + )]), } - } - } - CliAction::Edit { direction, file, line_number, floating } => Ok(vec![Action::EditFile(file, line_number, direction, floating)]), - CliAction::SwitchMode { input_mode } => Ok(vec![Action::SwitchModeForAllClients(input_mode)]), + }, + None => match floating { + Some(true) => Ok(vec![Action::NewFloatingPane(None)]), + _ => Ok(vec![Action::NewTiledPane(direction, None)]), + }, + }, + CliAction::Edit { + direction, + file, + line_number, + floating, + } => Ok(vec![Action::EditFile( + file, + line_number, + direction, + floating, + )]), + CliAction::SwitchMode { input_mode } => { + Ok(vec![Action::SwitchModeForAllClients(input_mode)]) + }, CliAction::TogglePaneEmbedOrFloating => Ok(vec![Action::TogglePaneEmbedOrFloating]), CliAction::ToggleFloatingPanes => Ok(vec![Action::ToggleFloatingPanes]), CliAction::ClosePane => Ok(vec![Action::CloseFocus]), - CliAction::RenamePane { name }=> Ok(vec![Action::PaneNameInput(name.as_bytes().to_vec())]), + CliAction::RenamePane { name } => { + Ok(vec![Action::PaneNameInput(name.as_bytes().to_vec())]) + }, CliAction::UndoRenamePane => Ok(vec![Action::UndoRenamePane]), CliAction::GoToNextTab => Ok(vec![Action::GoToNextTab]), CliAction::GoToPreviousTab => Ok(vec![Action::GoToPreviousTab]), @@ -298,15 +339,19 @@ impl Action { CliAction::GoToTab { index } => Ok(vec![Action::GoToTab(index)]), CliAction::RenameTab { name } => Ok(vec![ Action::TabNameInput(vec![0]), - Action::TabNameInput(name.as_bytes().to_vec()) + Action::TabNameInput(name.as_bytes().to_vec()), ]), CliAction::UndoRenameTab => Ok(vec![Action::UndoRenameTab]), - CliAction::SearchInput { input } => Ok(vec![Action::SearchInput(input.as_bytes().to_vec())]), + CliAction::SearchInput { input } => { + Ok(vec![Action::SearchInput(input.as_bytes().to_vec())]) + }, CliAction::SearchNext => Ok(vec![Action::Search(SearchDirection::Down)]), CliAction::SearchPrevious => Ok(vec![Action::Search(SearchDirection::Up)]), CliAction::NewTab { name, layout } => { if let Some(layout_path) = layout { - let (path_to_raw_layout, raw_layout) = Layout::stringified_from_path_or_default(Some(&layout_path), None).map_err(|e| format!("Failed to load layout: {}", e))?; + let (path_to_raw_layout, raw_layout) = + Layout::stringified_from_path_or_default(Some(&layout_path), None) + .map_err(|e| format!("Failed to load layout: {}", e))?; // let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| format!("Failed to parse layout: {}", e))?; let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| { let stringified_error = match e { @@ -349,7 +394,7 @@ impl Action { } else { Ok(vec![Action::NewTab(None, name)]) } - } + }, } } } diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index b7c7385fbc..2274d25191 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -1,16 +1,16 @@ +use crate::data::Palette; +use miette::{Diagnostic, LabeledSpan, NamedSource, SourceCode}; use std::fs::File; use std::io::{self, Read}; use std::path::PathBuf; use thiserror::Error; -use crate::data::Palette; -use miette::{Diagnostic, NamedSource, SourceCode, LabeledSpan}; use std::convert::TryFrom; use super::keybinds::Keybinds; use super::options::Options; use super::plugins::{PluginsConfig, PluginsConfigError}; -use super::theme::{UiConfig, Themes}; +use super::theme::{Themes, UiConfig}; use crate::cli::{CliArgs, Command}; use crate::envs::EnvironmentVariables; use crate::setup; @@ -53,7 +53,7 @@ impl KdlError { } } -impl std::fmt::Display for KdlError{ +impl std::fmt::Display for KdlError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!(f, "Failed to parse Zellij configuration") } @@ -64,7 +64,7 @@ impl Diagnostic for KdlError { fn source_code(&self) -> Option<&dyn SourceCode> { match self.src.as_ref() { Some(src) => Some(src), - None => None + None => None, } } fn help<'a>(&'a self) -> Option> { @@ -161,7 +161,7 @@ impl Config { pub fn theme_config(&self, opts: &Options) -> Option { match &opts.theme { Some(theme_name) => self.themes.get_theme(theme_name).map(|theme| theme.palette), - None => self.themes.get_theme("default").map(|theme| theme.palette) + None => self.themes.get_theme("default").map(|theme| theme.palette), } } /// Gets default configuration from assets @@ -169,8 +169,10 @@ impl Config { let cfg = String::from_utf8(setup::DEFAULT_CONFIG.to_vec())?; match Self::from_kdl(&cfg, None) { Ok(config) => Ok(config), - Err(ConfigError::KdlError(kdl_error)) => Err(ConfigError::KdlError(kdl_error.add_src("Default built-in-configuration".into(), cfg))), - Err(e) => Err(e) + Err(ConfigError::KdlError(kdl_error)) => Err(ConfigError::KdlError( + kdl_error.add_src("Default built-in-configuration".into(), cfg), + )), + Err(e) => Err(e), } } pub fn from_path(path: &PathBuf, default_config: Option) -> ConfigResult { @@ -186,18 +188,28 @@ impl Config { kdl::KdlErrorKind::Context("valid node terminator") => { format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") }, - _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), + _ => { + String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")) + }, }; let kdl_error = KdlError { error_message, - src: Some(NamedSource::new(path.as_path().as_os_str().to_string_lossy(), kdl_config)), + src: Some(NamedSource::new( + path.as_path().as_os_str().to_string_lossy(), + kdl_config, + )), offset: Some(kdl_error.span.offset()), len: Some(kdl_error.span.len()), }; Err(ConfigError::KdlError(kdl_error)) - } - Err(ConfigError::KdlError(kdl_error)) => Err(ConfigError::KdlError(kdl_error.add_src(path.as_path().as_os_str().to_string_lossy().to_string(), kdl_config))), - Err(e) => Err(e) + }, + Err(ConfigError::KdlError(kdl_error)) => { + Err(ConfigError::KdlError(kdl_error.add_src( + path.as_path().as_os_str().to_string_lossy().to_string(), + kdl_config, + ))) + }, + Err(e) => Err(e), } }, Err(e) => Err(ConfigError::IoPath(e, path.into())), @@ -207,15 +219,15 @@ impl Config { #[cfg(test)] mod config_test { - use std::io::Write; + use super::*; + use crate::data::{InputMode, Palette, PaletteColor, PluginTag}; + use crate::input::layout::RunPluginLocation; + use crate::input::options::{Clipboard, OnForceClose}; + use crate::input::plugins::{PluginConfig, PluginType, PluginsConfig}; + use crate::input::theme::{FrameConfig, Theme, Themes, UiConfig}; use std::collections::HashMap; - use crate::data::{Palette, PaletteColor, PluginTag, InputMode}; + use std::io::Write; use tempfile::tempdir; - use crate::input::layout::RunPluginLocation; - use crate::input::options::{OnForceClose, Clipboard}; - use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; - use crate::input::plugins::{PluginsConfig, PluginConfig, PluginType}; - use super::*; #[test] fn try_from_cli_args_with_config() { @@ -297,24 +309,96 @@ mod config_test { attach_to_session true "#; let config = Config::from_kdl(config_contents, None).unwrap(); - assert_eq!(config.options.simplified_ui, Some(true), "Option set in config"); - assert_eq!(config.options.theme, Some(String::from("my cool theme")), "Option set in config"); - assert_eq!(config.options.default_mode, Some(InputMode::Locked), "Option set in config"); - assert_eq!(config.options.default_shell, Some(PathBuf::from("/path/to/my/shell")), "Option set in config"); - assert_eq!(config.options.default_layout, Some(PathBuf::from("/path/to/my/layout.kdl")), "Option set in config"); - assert_eq!(config.options.layout_dir, Some(PathBuf::from("/path/to/my/layout-dir")), "Option set in config"); - assert_eq!(config.options.theme_dir, Some(PathBuf::from("/path/to/my/theme-dir")), "Option set in config"); - assert_eq!(config.options.mouse_mode, Some(false), "Option set in config"); - assert_eq!(config.options.pane_frames, Some(false), "Option set in config"); - assert_eq!(config.options.mirror_session, Some(true), "Option set in config"); - assert_eq!(config.options.on_force_close, Some(OnForceClose::Quit), "Option set in config"); - assert_eq!(config.options.scroll_buffer_size, Some(100000), "Option set in config"); - assert_eq!(config.options.copy_command, Some(String::from("/path/to/my/copy-command")), "Option set in config"); - assert_eq!(config.options.copy_clipboard, Some(Clipboard::Primary), "Option set in config"); - assert_eq!(config.options.copy_on_select, Some(false), "Option set in config"); - assert_eq!(config.options.scrollback_editor, Some(PathBuf::from("/path/to/my/scrollback-editor")), "Option set in config"); - assert_eq!(config.options.session_name, Some(String::from("my awesome session")), "Option set in config"); - assert_eq!(config.options.attach_to_session, Some(true), "Option set in config"); + assert_eq!( + config.options.simplified_ui, + Some(true), + "Option set in config" + ); + assert_eq!( + config.options.theme, + Some(String::from("my cool theme")), + "Option set in config" + ); + assert_eq!( + config.options.default_mode, + Some(InputMode::Locked), + "Option set in config" + ); + assert_eq!( + config.options.default_shell, + Some(PathBuf::from("/path/to/my/shell")), + "Option set in config" + ); + assert_eq!( + config.options.default_layout, + Some(PathBuf::from("/path/to/my/layout.kdl")), + "Option set in config" + ); + assert_eq!( + config.options.layout_dir, + Some(PathBuf::from("/path/to/my/layout-dir")), + "Option set in config" + ); + assert_eq!( + config.options.theme_dir, + Some(PathBuf::from("/path/to/my/theme-dir")), + "Option set in config" + ); + assert_eq!( + config.options.mouse_mode, + Some(false), + "Option set in config" + ); + assert_eq!( + config.options.pane_frames, + Some(false), + "Option set in config" + ); + assert_eq!( + config.options.mirror_session, + Some(true), + "Option set in config" + ); + assert_eq!( + config.options.on_force_close, + Some(OnForceClose::Quit), + "Option set in config" + ); + assert_eq!( + config.options.scroll_buffer_size, + Some(100000), + "Option set in config" + ); + assert_eq!( + config.options.copy_command, + Some(String::from("/path/to/my/copy-command")), + "Option set in config" + ); + assert_eq!( + config.options.copy_clipboard, + Some(Clipboard::Primary), + "Option set in config" + ); + assert_eq!( + config.options.copy_on_select, + Some(false), + "Option set in config" + ); + assert_eq!( + config.options.scrollback_editor, + Some(PathBuf::from("/path/to/my/scrollback-editor")), + "Option set in config" + ); + assert_eq!( + config.options.session_name, + Some(String::from("my awesome session")), + "Option set in config" + ); + assert_eq!( + config.options.attach_to_session, + Some(true), + "Option set in config" + ); } #[test] @@ -338,22 +422,25 @@ mod config_test { "#; let config = Config::from_kdl(config_contents, None).unwrap(); let mut expected_themes = HashMap::new(); - expected_themes.insert("dracula".into(), Theme { - palette: Palette { - fg: PaletteColor::Rgb((248, 248, 242)), - bg: PaletteColor::Rgb((40, 42, 54)), - red: PaletteColor::Rgb((255, 85, 85)), - green: PaletteColor::Rgb((80, 250, 123)), - yellow: PaletteColor::Rgb((241, 250, 140)), - blue: PaletteColor::Rgb((98, 114, 164)), - magenta: PaletteColor::Rgb((255, 121, 198)), - orange: PaletteColor::Rgb((255, 184, 108)), - cyan: PaletteColor::Rgb((139, 233, 253)), - black: PaletteColor::Rgb((0, 0, 0)), - white: PaletteColor::Rgb((255, 255, 255)), - ..Default::default() - } - }); + expected_themes.insert( + "dracula".into(), + Theme { + palette: Palette { + fg: PaletteColor::Rgb((248, 248, 242)), + bg: PaletteColor::Rgb((40, 42, 54)), + red: PaletteColor::Rgb((255, 85, 85)), + green: PaletteColor::Rgb((80, 250, 123)), + yellow: PaletteColor::Rgb((241, 250, 140)), + blue: PaletteColor::Rgb((98, 114, 164)), + magenta: PaletteColor::Rgb((255, 121, 198)), + orange: PaletteColor::Rgb((255, 184, 108)), + cyan: PaletteColor::Rgb((139, 233, 253)), + black: PaletteColor::Rgb((0, 0, 0)), + white: PaletteColor::Rgb((255, 255, 255)), + ..Default::default() + }, + }, + ); let expected_themes = Themes::from_data(expected_themes); assert_eq!(config.themes, expected_themes, "Theme defined in config"); } @@ -392,38 +479,44 @@ mod config_test { "##; let config = Config::from_kdl(config_contents, None).unwrap(); let mut expected_themes = HashMap::new(); - expected_themes.insert("dracula".into(), Theme { - palette: Palette { - fg: PaletteColor::Rgb((248, 248, 242)), - bg: PaletteColor::Rgb((40, 42, 54)), - red: PaletteColor::Rgb((255, 85, 85)), - green: PaletteColor::Rgb((80, 250, 123)), - yellow: PaletteColor::Rgb((241, 250, 140)), - blue: PaletteColor::Rgb((98, 114, 164)), - magenta: PaletteColor::Rgb((255, 121, 198)), - orange: PaletteColor::Rgb((255, 184, 108)), - cyan: PaletteColor::Rgb((139, 233, 253)), - black: PaletteColor::Rgb((0, 0, 0)), - white: PaletteColor::Rgb((255, 255, 255)), - ..Default::default() - } - }); - expected_themes.insert("nord".into(), Theme { - palette: Palette { - fg: PaletteColor::Rgb((216, 222, 233)), - bg: PaletteColor::Rgb((46, 52, 64)), - black: PaletteColor::Rgb((59, 66, 82)), - red: PaletteColor::Rgb((191, 97, 106)), - green: PaletteColor::Rgb((163, 190, 140)), - yellow: PaletteColor::Rgb((235, 203, 139)), - blue: PaletteColor::Rgb((129, 161, 193)), - magenta: PaletteColor::Rgb((180, 142, 173)), - cyan: PaletteColor::Rgb((136, 192, 208)), - white: PaletteColor::Rgb((229, 233, 240)), - orange: PaletteColor::Rgb((208, 135, 112)), - ..Default::default() - } - }); + expected_themes.insert( + "dracula".into(), + Theme { + palette: Palette { + fg: PaletteColor::Rgb((248, 248, 242)), + bg: PaletteColor::Rgb((40, 42, 54)), + red: PaletteColor::Rgb((255, 85, 85)), + green: PaletteColor::Rgb((80, 250, 123)), + yellow: PaletteColor::Rgb((241, 250, 140)), + blue: PaletteColor::Rgb((98, 114, 164)), + magenta: PaletteColor::Rgb((255, 121, 198)), + orange: PaletteColor::Rgb((255, 184, 108)), + cyan: PaletteColor::Rgb((139, 233, 253)), + black: PaletteColor::Rgb((0, 0, 0)), + white: PaletteColor::Rgb((255, 255, 255)), + ..Default::default() + }, + }, + ); + expected_themes.insert( + "nord".into(), + Theme { + palette: Palette { + fg: PaletteColor::Rgb((216, 222, 233)), + bg: PaletteColor::Rgb((46, 52, 64)), + black: PaletteColor::Rgb((59, 66, 82)), + red: PaletteColor::Rgb((191, 97, 106)), + green: PaletteColor::Rgb((163, 190, 140)), + yellow: PaletteColor::Rgb((235, 203, 139)), + blue: PaletteColor::Rgb((129, 161, 193)), + magenta: PaletteColor::Rgb((180, 142, 173)), + cyan: PaletteColor::Rgb((136, 192, 208)), + white: PaletteColor::Rgb((229, 233, 240)), + orange: PaletteColor::Rgb((208, 135, 112)), + ..Default::default() + }, + }, + ); let expected_themes = Themes::from_data(expected_themes); assert_eq!(config.themes, expected_themes, "Theme defined in config"); } @@ -449,22 +542,25 @@ mod config_test { "#; let config = Config::from_kdl(config_contents, None).unwrap(); let mut expected_themes = HashMap::new(); - expected_themes.insert("eight_bit_theme".into(), Theme { - palette: Palette { - fg: PaletteColor::EightBit(248), - bg: PaletteColor::EightBit(40), - red: PaletteColor::EightBit(255), - green: PaletteColor::EightBit(80), - yellow: PaletteColor::EightBit(241), - blue: PaletteColor::EightBit(98), - magenta: PaletteColor::EightBit(255), - orange: PaletteColor::EightBit(255), - cyan: PaletteColor::EightBit(139), - black: PaletteColor::EightBit(1), - white: PaletteColor::EightBit(255), - ..Default::default() - } - }); + expected_themes.insert( + "eight_bit_theme".into(), + Theme { + palette: Palette { + fg: PaletteColor::EightBit(248), + bg: PaletteColor::EightBit(40), + red: PaletteColor::EightBit(255), + green: PaletteColor::EightBit(80), + yellow: PaletteColor::EightBit(241), + blue: PaletteColor::EightBit(98), + magenta: PaletteColor::EightBit(255), + orange: PaletteColor::EightBit(255), + cyan: PaletteColor::EightBit(139), + black: PaletteColor::EightBit(1), + white: PaletteColor::EightBit(255), + ..Default::default() + }, + }, + ); let expected_themes = Themes::from_data(expected_themes); assert_eq!(config.themes, expected_themes, "Theme defined in config"); } @@ -484,31 +580,47 @@ mod config_test { "#; let config = Config::from_kdl(config_contents, None).unwrap(); let mut expected_plugin_configuration = HashMap::new(); - expected_plugin_configuration.insert(PluginTag::new("tab-bar"), PluginConfig { - path: PathBuf::from("tab-bar"), - run: PluginType::Pane(None), - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false - }); - expected_plugin_configuration.insert(PluginTag::new("status-bar"), PluginConfig { - path: PathBuf::from("status-bar"), - run: PluginType::Pane(None), - location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), - _allow_exec_host_cmd: false - }); - expected_plugin_configuration.insert(PluginTag::new("strider"), PluginConfig { - path: PathBuf::from("strider"), - run: PluginType::Pane(None), - location: RunPluginLocation::Zellij(PluginTag::new("strider")), - _allow_exec_host_cmd: true - }); - expected_plugin_configuration.insert(PluginTag::new("compact-bar"), PluginConfig { - path: PathBuf::from("compact-bar"), - run: PluginType::Pane(None), - location: RunPluginLocation::Zellij(PluginTag::new("compact-bar")), - _allow_exec_host_cmd: false - }); - assert_eq!(config.plugins, PluginsConfig::from_data(expected_plugin_configuration), "Plugins defined in config"); + expected_plugin_configuration.insert( + PluginTag::new("tab-bar"), + PluginConfig { + path: PathBuf::from("tab-bar"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + _allow_exec_host_cmd: false, + }, + ); + expected_plugin_configuration.insert( + PluginTag::new("status-bar"), + PluginConfig { + path: PathBuf::from("status-bar"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("status-bar")), + _allow_exec_host_cmd: false, + }, + ); + expected_plugin_configuration.insert( + PluginTag::new("strider"), + PluginConfig { + path: PathBuf::from("strider"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("strider")), + _allow_exec_host_cmd: true, + }, + ); + expected_plugin_configuration.insert( + PluginTag::new("compact-bar"), + PluginConfig { + path: PathBuf::from("compact-bar"), + run: PluginType::Pane(None), + location: RunPluginLocation::Zellij(PluginTag::new("compact-bar")), + _allow_exec_host_cmd: false, + }, + ); + assert_eq!( + config.plugins, + PluginsConfig::from_data(expected_plugin_configuration), + "Plugins defined in config" + ); } #[test] @@ -523,8 +635,8 @@ mod config_test { let config = Config::from_kdl(config_contents, None).unwrap(); let expected_ui_config = UiConfig { pane_frames: FrameConfig { - rounded_corners: true - } + rounded_corners: true, + }, }; assert_eq!(config.ui, expected_ui_config, "Ui config defined in config"); } @@ -541,7 +653,10 @@ mod config_test { let mut expected_env_config = HashMap::new(); expected_env_config.insert("RUST_BACKTRACE".into(), "1".into()); expected_env_config.insert("SOME_OTHER_VAR".into(), "foo".into()); - assert_eq!(config.env, EnvironmentVariables::from_data(expected_env_config), "Env variables defined in config"); + assert_eq!( + config.env, + EnvironmentVariables::from_data(expected_env_config), + "Env variables defined in config" + ); } - } diff --git a/zellij-utils/src/input/keybinds.rs b/zellij-utils/src/input/keybinds.rs index b98a8e6342..eaed03fd42 100644 --- a/zellij-utils/src/input/keybinds.rs +++ b/zellij-utils/src/input/keybinds.rs @@ -26,16 +26,21 @@ impl fmt::Debug for Keybinds { impl Keybinds { pub fn get_actions_for_key_in_mode(&self, mode: &InputMode, key: &Key) -> Option<&Vec> { - self.0.get(mode) + self.0 + .get(mode) .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key)) } - pub fn get_actions_for_key_in_mode_or_default_action(&self, mode: &InputMode, key: &Key, raw_bytes: Vec) -> Vec { - self.0.get(mode) + pub fn get_actions_for_key_in_mode_or_default_action( + &self, + mode: &InputMode, + key: &Key, + raw_bytes: Vec, + ) -> Vec { + self.0 + .get(mode) .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key)) .cloned() - .unwrap_or_else(|| { - vec![self.default_action_for_mode(mode, raw_bytes)] - }) + .unwrap_or_else(|| vec![self.default_action_for_mode(mode, raw_bytes)]) } pub fn get_input_mode_mut(&mut self, input_mode: &InputMode) -> &mut HashMap> { self.0.entry(*input_mode).or_insert_with(HashMap::new) diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 6ea4c48e84..eea80859f6 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -17,13 +17,9 @@ use crate::{ setup, }; -use kdl::*; - use std::str::FromStr; -use super::{ - plugins::{PluginTag, PluginsConfigError}, -}; +use super::plugins::{PluginTag, PluginsConfigError}; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use std::vec::Vec; @@ -128,11 +124,15 @@ pub struct PaneLayout { } impl PaneLayout { - pub fn insert_children_layout(&mut self, children_layout: &mut PaneLayout) -> Result { + pub fn insert_children_layout( + &mut self, + children_layout: &mut PaneLayout, + ) -> Result { // returns true if successfully inserted and false otherwise match self.external_children_index { Some(external_children_index) => { - self.children.insert(external_children_index, children_layout.clone()); + self.children + .insert(external_children_index, children_layout.clone()); self.external_children_index = None; Ok(true) }, @@ -143,7 +143,7 @@ impl PaneLayout { } } Ok(false) - } + }, } } pub fn children_block_count(&self) -> usize { @@ -186,12 +186,8 @@ pub enum LayoutParts { impl LayoutParts { pub fn is_empty(&self) -> bool { match self { - LayoutParts::Panes(panes) => { - panes.is_empty() - }, - LayoutParts::Tabs(tabs) => { - tabs.is_empty() - } + LayoutParts::Panes(panes) => panes.is_empty(), + LayoutParts::Tabs(tabs) => tabs.is_empty(), } } pub fn insert_pane(&mut self, index: usize, layout: Layout) -> Result<(), ConfigError> { @@ -200,9 +196,11 @@ impl LayoutParts { panes.insert(index, layout); Ok(()) }, - LayoutParts::Tabs(_tabs) => { - Err(ConfigError::new_kdl_error("Trying to insert a pane into a tab layout".into(), 0, 0)) - } + LayoutParts::Tabs(_tabs) => Err(ConfigError::new_kdl_error( + "Trying to insert a pane into a tab layout".into(), + 0, + 0, + )), } } } @@ -214,7 +212,11 @@ impl Default for LayoutParts { } impl Layout { - pub fn stringified_from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) + pub fn stringified_from_path_or_default( + layout_path: Option<&PathBuf>, + layout_dir: Option, + ) -> Result<(String, String), ConfigError> { + // (path_to_layout as String, stringified_layout) match layout_path { Some(layout_path) => { // The way we determine where to look for the layout is similar to @@ -228,16 +230,19 @@ impl Layout { Layout::stringified_from_dir(layout_path, layout_dir.as_ref()) } }, - None => { - Layout::stringified_from_dir( - &std::path::PathBuf::from("default"), - layout_dir.as_ref(), - ) - } + None => Layout::stringified_from_dir( + &std::path::PathBuf::from("default"), + layout_dir.as_ref(), + ), } } - pub fn from_path_or_default(layout_path: Option<&PathBuf>, layout_dir: Option, config: Config) -> Result<(Layout, Config), ConfigError> { - let (path_to_raw_layout, raw_layout) = Layout::stringified_from_path_or_default(layout_path, layout_dir)?; + pub fn from_path_or_default( + layout_path: Option<&PathBuf>, + layout_dir: Option, + config: Config, + ) -> Result<(Layout, Config), ConfigError> { + let (path_to_raw_layout, raw_layout) = + Layout::stringified_from_path_or_default(layout_path, layout_dir)?; let layout = Layout::from_kdl(&raw_layout, path_to_raw_layout)?; let config = Config::from_kdl(&raw_layout, Some(config))?; // this merges the two config, with Ok((layout, config)) @@ -248,7 +253,8 @@ impl Layout { pub fn stringified_from_dir( layout: &PathBuf, layout_dir: Option<&PathBuf>, - ) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) + ) -> Result<(String, String), ConfigError> { + // (path_to_layout as String, stringified_layout) match layout_dir { Some(dir) => { let layout_path = &dir.join(layout); @@ -261,7 +267,8 @@ impl Layout { None => Layout::stringified_from_default_assets(layout), } } - pub fn stringified_from_path(layout_path: &Path) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) + pub fn stringified_from_path(layout_path: &Path) -> Result<(String, String), ConfigError> { + // (path_to_layout as String, stringified_layout) let mut layout_file = File::open(&layout_path) .or_else(|_| File::open(&layout_path.with_extension("kdl"))) .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; @@ -270,15 +277,28 @@ impl Layout { layout_file.read_to_string(&mut kdl_layout)?; Ok((layout_path.as_os_str().to_string_lossy().into(), kdl_layout)) } - pub fn stringified_from_default_assets(path: &Path) -> Result<(String, String), ConfigError> { // (path_to_layout as String, stringified_layout) + pub fn stringified_from_default_assets(path: &Path) -> Result<(String, String), ConfigError> { + // (path_to_layout as String, stringified_layout) // TODO: ideally these should not be hard-coded // we should load layouts by name from the config // and load them from a hashmap or some such match path.to_str() { - Some("default") => Ok(("Default layout".into(), Self::stringified_default_from_assets()?)), - Some("strider") => Ok(("Strider layout".into(), Self::stringified_strider_from_assets()?)), - Some("disable-status-bar") => Ok(("Disable Status Bar layout".into(), Self::stringified_disable_status_from_assets()?)), - Some("compact") => Ok(("Compact layout".into(), Self::stringified_compact_from_assets()?)), + Some("default") => Ok(( + "Default layout".into(), + Self::stringified_default_from_assets()?, + )), + Some("strider") => Ok(( + "Strider layout".into(), + Self::stringified_strider_from_assets()?, + )), + Some("disable-status-bar") => Ok(( + "Disable Status Bar layout".into(), + Self::stringified_disable_status_from_assets()?, + )), + Some("compact") => Ok(( + "Compact layout".into(), + Self::stringified_compact_from_assets()?, + )), None | Some(_) => Err(ConfigError::IoPath( std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), path.into(), @@ -304,7 +324,7 @@ impl Layout { pub fn new_tab(&self) -> PaneLayout { match &self.template { Some(template) => template.clone(), - None => PaneLayout::default() + None => PaneLayout::default(), } } @@ -316,26 +336,45 @@ impl Layout { !self.tabs.is_empty() } - pub fn tabs(&self) -> Vec<(Option, PaneLayout)> { // String is the tab name + pub fn tabs(&self) -> Vec<(Option, PaneLayout)> { + // String is the tab name self.tabs.clone() } pub fn focused_tab_index(&self) -> Option { self.focused_tab_index } - } -fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_split: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> { +fn split_space( + space_to_split: &PaneGeom, + layout: &PaneLayout, + total_space_to_split: &PaneGeom, +) -> Vec<(PaneLayout, PaneGeom)> { let mut pane_positions = Vec::new(); - let sizes: Vec> = layout.children.iter().map(|part| part.split_size).collect(); + let sizes: Vec> = + layout.children.iter().map(|part| part.split_size).collect(); let mut split_geom = Vec::new(); - let (mut current_position, split_dimension_space, inherited_dimension, total_split_dimension_space) = - match layout.children_split_direction { - SplitDirection::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows, total_space_to_split.cols), - SplitDirection::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols, total_space_to_split.rows), - }; + let ( + mut current_position, + split_dimension_space, + inherited_dimension, + total_split_dimension_space, + ) = match layout.children_split_direction { + SplitDirection::Vertical => ( + space_to_split.x, + space_to_split.cols, + space_to_split.rows, + total_space_to_split.cols, + ), + SplitDirection::Horizontal => ( + space_to_split.y, + space_to_split.rows, + space_to_split.cols, + total_space_to_split.rows, + ), + }; let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); @@ -348,11 +387,9 @@ fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_sp let free_percent = if let Some(p) = split_dimension_space.as_percent() { p - sizes .iter() - .map(|&s| { - match s { - Some(SplitSize::Percent(ip)) => ip as f64, - _ => 0.0, - } + .map(|&s| match s { + Some(SplitSize::Percent(ip)) => ip as f64, + _ => 0.0, }) .sum::() } else { @@ -395,7 +432,8 @@ fn split_space(space_to_split: &PaneGeom, layout: &PaneLayout, total_space_to_sp for (i, part) in layout.children.iter().enumerate() { let part_position_and_size = split_geom.get(i).unwrap(); if !part.children.is_empty() { - let mut part_positions = split_space(part_position_and_size, part, total_space_to_split); + let mut part_positions = + split_space(part_position_and_size, part, total_space_to_split); pane_positions.append(&mut part_positions); } else { pane_positions.push((part.clone(), *part_position_and_size)); diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index e722cb5b9b..60707597ca 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -171,9 +171,7 @@ impl Options { let scrollback_editor = other .scrollback_editor .or_else(|| self.scrollback_editor.clone()); - let session_name = other - .session_name - .or_else(|| self.session_name.clone()); + let session_name = other.session_name.or_else(|| self.session_name.clone()); let attach_to_session = other .attach_to_session .or_else(|| self.attach_to_session.clone()); @@ -234,9 +232,7 @@ impl Options { let scrollback_editor = other .scrollback_editor .or_else(|| self.scrollback_editor.clone()); - let session_name = other - .session_name - .or_else(|| self.session_name.clone()); + let session_name = other.session_name.or_else(|| self.session_name.clone()); let attach_to_session = other .attach_to_session .or_else(|| self.attach_to_session.clone()); diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index 60310752ba..55ea428386 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -11,8 +11,8 @@ use url::Url; use super::layout::{RunPlugin, RunPluginLocation}; pub use crate::data::PluginTag; -use std::fmt; use std::collections::BTreeMap; +use std::fmt; /// Used in the config struct for plugin metadata #[derive(Clone, PartialEq, Deserialize, Serialize)] diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index 408737525b..c76deb31fb 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -2,7 +2,10 @@ use serde::{ de::{Error, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; -use std::{collections::{HashMap, BTreeMap}, fmt}; +use std::{ + collections::{BTreeMap, HashMap}, + fmt, +}; use crate::data::Palette; diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index c69960776d..57126d595a 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -1,7 +1,7 @@ use super::super::actions::*; use super::super::keybinds::*; -use crate::input::config::Config; use crate::data::{self, CharOrArrow, Key}; +use crate::input::config::Config; use insta::assert_snapshot; use strum::IntoEnumIterator; @@ -18,7 +18,11 @@ fn can_define_keybindings_in_configfile() { let ctrl_g_normal_mode_action = config .keybinds .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); - assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding successfully defined in config"); + assert_eq!( + ctrl_g_normal_mode_action, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybinding successfully defined in config" + ); } #[test] @@ -31,20 +35,23 @@ fn can_define_multiple_keybinds_for_same_action() { } "#; let config = Config::from_kdl(config_contents, None).unwrap(); - let alt_h_normal_mode_action = config - .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Alt(CharOrArrow::Direction(data::Direction::Left)) - ); + let alt_h_normal_mode_action = config.keybinds.get_actions_for_key_in_mode( + &InputMode::Normal, + &Key::Alt(CharOrArrow::Direction(data::Direction::Left)), + ); let alt_left_normal_mode_action = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Alt(CharOrArrow::Char('h')) - ); - assert_eq!(alt_h_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "First keybinding successfully defined in config"); - assert_eq!(alt_left_normal_mode_action, Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), "Second keybinding successfully defined in config"); + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Alt(CharOrArrow::Char('h'))); + assert_eq!( + alt_h_normal_mode_action, + Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), + "First keybinding successfully defined in config" + ); + assert_eq!( + alt_left_normal_mode_action, + Some(&vec![Action::MoveFocusOrTab(Direction::Left)]), + "Second keybinding successfully defined in config" + ); } #[test] @@ -59,11 +66,15 @@ fn can_define_series_of_actions_for_same_keybinding() { let config = Config::from_kdl(config_contents, None).unwrap(); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Action series successfully defined"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); + assert_eq!( + z_in_pane_mode, + Some(&vec![ + Action::TogglePaneFrames, + Action::SwitchToMode(InputMode::Normal) + ]), + "Action series successfully defined" + ); } #[test] @@ -79,11 +90,12 @@ fn keybindings_bind_order_is_preserved() { let config = Config::from_kdl(config_contents, None).unwrap(); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Second keybinding was applied"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); + assert_eq!( + z_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Resize)]), + "Second keybinding was applied" + ); } #[test] @@ -99,18 +111,23 @@ fn uppercase_and_lowercase_keybindings_are_distinct() { let config = Config::from_kdl(config_contents, None).unwrap(); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); let uppercase_z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('Z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Lowercase z successfully bound"); - assert_eq!(uppercase_z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Uppercase z successfully bound"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('Z')); + assert_eq!( + z_in_pane_mode, + Some(&vec![ + Action::TogglePaneFrames, + Action::SwitchToMode(InputMode::Normal) + ]), + "Lowercase z successfully bound" + ); + assert_eq!( + uppercase_z_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Resize)]), + "Uppercase z successfully bound" + ); } #[test] @@ -133,11 +150,12 @@ fn can_override_keybindings() { let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from config overrode keybinding from default config"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); + assert_eq!( + z_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Resize)]), + "Keybinding from config overrode keybinding from default config" + ); } #[test] @@ -162,18 +180,23 @@ fn can_add_to_default_keybindings() { let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); let r_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - assert_eq!(z_in_pane_mode, Some(&vec![Action::TogglePaneFrames, Action::SwitchToMode(InputMode::Normal)]), "Keybinding from default config bound"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from custom config bound as well"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r')); + assert_eq!( + z_in_pane_mode, + Some(&vec![ + Action::TogglePaneFrames, + Action::SwitchToMode(InputMode::Normal) + ]), + "Keybinding from default config bound" + ); + assert_eq!( + r_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Resize)]), + "Keybinding from custom config bound as well" + ); } #[test] @@ -205,26 +228,31 @@ fn can_clear_default_keybindings() { .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); let ctrl_r_in_normal_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Ctrl('r'), - ); + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('r')); let r_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - assert_eq!(ctrl_g_normal_mode_action, None, "Keybinding from normal mode in default config cleared"); - assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); - assert_eq!(ctrl_r_in_normal_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybinding from normal mode in custom config still bound"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r')); + assert_eq!( + ctrl_g_normal_mode_action, None, + "Keybinding from normal mode in default config cleared" + ); + assert_eq!( + z_in_pane_mode, None, + "Keybinding from pane mode in default config cleared" + ); + assert_eq!( + r_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Resize)]), + "Keybinding from pane mode in custom config still bound" + ); + assert_eq!( + ctrl_r_in_normal_mode, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybinding from normal mode in custom config still bound" + ); } #[test] @@ -250,25 +278,27 @@ fn can_clear_default_keybindings_per_single_mode() { let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); let ctrl_g_normal_mode_action = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Ctrl('g'), - ); + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); let r_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); - assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode from default config not cleared"); - assert_eq!(z_in_pane_mode, None, "Keybinding from pane mode in default config cleared"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Resize)]), "Keybinding from pane mode in custom config still bound"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r')); + assert_eq!( + ctrl_g_normal_mode_action, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybind in different mode from default config not cleared" + ); + assert_eq!( + z_in_pane_mode, None, + "Keybinding from pane mode in default config cleared" + ); + assert_eq!( + r_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Resize)]), + "Keybinding from pane mode in custom config still bound" + ); } #[test] @@ -297,39 +327,38 @@ fn can_unbind_multiple_keys_globally() { let config = Config::from_kdl(config_contents, Some(default_config)).unwrap(); let ctrl_g_normal_mode_action = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Normal, - &Key::Ctrl('g'), - ); + .get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g')); let ctrl_g_pane_mode_action = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Ctrl('g'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g')); let r_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r')); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); let t_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('t'), - ); - assert_eq!(ctrl_g_normal_mode_action, None, "First keybind uncleared in one mode"); - assert_eq!(ctrl_g_pane_mode_action, None, "First keybind uncleared in another mode"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('t')); + assert_eq!( + ctrl_g_normal_mode_action, None, + "First keybind uncleared in one mode" + ); + assert_eq!( + ctrl_g_pane_mode_action, None, + "First keybind uncleared in another mode" + ); assert_eq!(z_in_pane_mode, None, "Second keybind cleared as well"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); - assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); + assert_eq!( + r_in_pane_mode, + Some(&vec![Action::TogglePaneFrames]), + "Unrelated keybinding in default config still bound" + ); + assert_eq!( + t_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Tab)]), + "Keybinding from custom config still bound" + ); } #[test] @@ -364,27 +393,36 @@ fn can_unbind_multiple_keys_per_single_mode() { .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g')); let r_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('r'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r')); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); let t_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('t'), - ); - assert_eq!(ctrl_g_normal_mode_action, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind in different mode not cleared"); - assert_eq!(ctrl_g_pane_mode_action, None, "First Keybind cleared in its mode"); - assert_eq!(z_in_pane_mode, None, "Second keybind cleared in its mode as well"); - assert_eq!(r_in_pane_mode, Some(&vec![Action::TogglePaneFrames]), "Unrelated keybinding in default config still bound"); - assert_eq!(t_in_pane_mode, Some(&vec![Action::SwitchToMode(InputMode::Tab)]), "Keybinding from custom config still bound"); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('t')); + assert_eq!( + ctrl_g_normal_mode_action, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybind in different mode not cleared" + ); + assert_eq!( + ctrl_g_pane_mode_action, None, + "First Keybind cleared in its mode" + ); + assert_eq!( + z_in_pane_mode, None, + "Second keybind cleared in its mode as well" + ); + assert_eq!( + r_in_pane_mode, + Some(&vec![Action::TogglePaneFrames]), + "Unrelated keybinding in default config still bound" + ); + assert_eq!( + t_in_pane_mode, + Some(&vec![Action::SwitchToMode(InputMode::Tab)]), + "Keybinding from custom config still bound" + ); } #[test] @@ -398,8 +436,14 @@ fn can_define_shared_keybinds_for_all_modes() { "#; let config = Config::from_kdl(config_contents, None).unwrap(); for mode in InputMode::iter() { - let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); - assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); + let action_in_mode = config + .keybinds + .get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + assert_eq!( + action_in_mode, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybind bound in mode" + ); } } @@ -414,11 +458,17 @@ fn can_define_shared_keybinds_with_exclusion() { "#; let config = Config::from_kdl(config_contents, None).unwrap(); for mode in InputMode::iter() { - let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + let action_in_mode = config + .keybinds + .get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); if mode == InputMode::Locked { assert_eq!(action_in_mode, None, "Keybind unbound in excluded mode"); } else { - assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in mode"); + assert_eq!( + action_in_mode, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybind bound in mode" + ); } } } @@ -434,9 +484,15 @@ fn can_define_shared_keybinds_with_inclusion() { "#; let config = Config::from_kdl(config_contents, None).unwrap(); for mode in InputMode::iter() { - let action_in_mode = config.keybinds.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); + let action_in_mode = config + .keybinds + .get_actions_for_key_in_mode(&mode, &Key::Ctrl('g')); if mode == InputMode::Normal || mode == InputMode::Resize || mode == InputMode::Pane { - assert_eq!(action_in_mode, Some(&vec![Action::SwitchToMode(InputMode::Locked)]), "Keybind bound in included mode"); + assert_eq!( + action_in_mode, + Some(&vec![Action::SwitchToMode(InputMode::Locked)]), + "Keybind bound in included mode" + ); } else { assert_eq!(action_in_mode, None, "Keybind unbound in other modes"); } @@ -456,10 +512,7 @@ fn keybindings_unbinds_happen_after_binds() { let config = Config::from_kdl(config_contents, None).unwrap(); let z_in_pane_mode = config .keybinds - .get_actions_for_key_in_mode( - &InputMode::Pane, - &Key::Char('z'), - ); + .get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z')); assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound"); } diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index b8457a74bb..b4b309ba87 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -74,19 +74,13 @@ fn layout_with_nested_panes() { children: vec![ PaneLayout { children_split_direction: SplitDirection::Vertical, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], + children: vec![PaneLayout::default(), PaneLayout::default()], ..Default::default() }, PaneLayout { - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], + children: vec![PaneLayout::default(), PaneLayout::default()], ..Default::default() - } + }, ], ..Default::default() }), @@ -104,9 +98,7 @@ fn layout_with_tabs() { "#; let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { - tabs: vec![ - (None, PaneLayout::default()), - ], + tabs: vec![(None, PaneLayout::default())], template: Some(PaneLayout::default()), ..Default::default() }; @@ -131,23 +123,26 @@ fn layout_with_nested_differing_tabs() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ - (None, PaneLayout { - children_split_direction: SplitDirection::Vertical, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }), - (None, PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }), + ( + None, + PaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], + ..Default::default() + }, + ), + ( + None, + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![PaneLayout::default(), PaneLayout::default()], + ..Default::default() + }, + ), ], template: Some(PaneLayout::default()), ..Default::default() @@ -203,15 +198,13 @@ fn layout_with_command_panes() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { - children: vec![ - PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - ..Default::default() - })), + children: vec![PaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), ..Default::default() - } - ], + })), + ..Default::default() + }], ..Default::default() }), ..Default::default() @@ -228,21 +221,17 @@ fn layout_with_command_panes_and_cwd() { "#; let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { - template: Some( - PaneLayout { - children: vec![ - PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - cwd: Some(PathBuf::from("/path/to/my/cwd")), - ..Default::default() - })), - ..Default::default() - } - ], + template: Some(PaneLayout { + children: vec![PaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + ..Default::default() + })), ..Default::default() - }, - ), + }], + ..Default::default() + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -259,22 +248,18 @@ fn layout_with_command_panes_and_cwd_and_args() { "#; let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { - template: Some( - PaneLayout { - children: vec![ - PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - cwd: Some(PathBuf::from("/path/to/my/cwd")), - args: vec![String::from("-h"), String::from("-v")], - ..Default::default() - })), - ..Default::default() - } - ], + template: Some(PaneLayout { + children: vec![PaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + args: vec![String::from("-h"), String::from("-v")], + ..Default::default() + })), ..Default::default() - }, - ), + }], + ..Default::default() + }), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -328,12 +313,10 @@ fn layout_with_borderless_panes() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { - children: vec![ - PaneLayout { - borderless: true, - ..Default::default() - }, - ], + children: vec![PaneLayout { + borderless: true, + ..Default::default() + }], ..Default::default() }), ..Default::default() @@ -351,12 +334,10 @@ fn layout_with_focused_panes() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { - children: vec![ - PaneLayout { - focus: Some(true), - ..Default::default() - }, - ], + children: vec![PaneLayout { + focus: Some(true), + ..Default::default() + }], ..Default::default() }), ..Default::default() @@ -374,12 +355,10 @@ fn layout_with_pane_names() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { template: Some(PaneLayout { - children: vec![ - PaneLayout { - name: Some("my awesome pane".into()), - ..Default::default() - }, - ], + children: vec![PaneLayout { + name: Some("my awesome pane".into()), + ..Default::default() + }], ..Default::default() }), ..Default::default() @@ -398,14 +377,20 @@ fn layout_with_tab_names() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ - (Some("my cool tab name 1".into()), PaneLayout { - children: vec![], - ..Default::default() - }), - (Some("my cool tab name 2".into()), PaneLayout { - children: vec![], - ..Default::default() - }), + ( + Some("my cool tab name 1".into()), + PaneLayout { + children: vec![], + ..Default::default() + }, + ), + ( + Some("my cool tab name 2".into()), + PaneLayout { + children: vec![], + ..Default::default() + }, + ), ], template: Some(PaneLayout::default()), ..Default::default() @@ -459,47 +444,50 @@ fn layout_with_tab_templates() { let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap(); let expected_layout = Layout { tabs: vec![ - (Some("my first tab".into()), PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout { - children_split_direction: SplitDirection::Vertical, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }, - PaneLayout::default(), - ], - ..Default::default() - }), - (Some("my second tab".into()), PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }, - PaneLayout::default(), - ], - ..Default::default() - }), - (None, PaneLayout { - children_split_direction: SplitDirection::Horizontal, - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }), + ( + Some("my first tab".into()), + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![PaneLayout::default(), PaneLayout::default()], + ..Default::default() + }, + PaneLayout::default(), + ], + ..Default::default() + }, + ), + ( + Some("my second tab".into()), + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![PaneLayout::default(), PaneLayout::default()], + ..Default::default() + }, + PaneLayout::default(), + ], + ..Default::default() + }, + ), + ( + None, + PaneLayout { + children_split_direction: SplitDirection::Horizontal, + children: vec![ + PaneLayout::default(), + PaneLayout::default(), + PaneLayout::default(), + ], + ..Default::default() + }, + ), ], template: Some(PaneLayout::default()), ..Default::default() @@ -692,7 +680,10 @@ fn error_on_more_than_one_children_block_in_tab_template() { } "#; let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); - assert!(layout.is_err(), "error provided for more than one children block"); + assert!( + layout.is_err(), + "error provided for more than one children block" + ); } #[test] @@ -737,7 +728,10 @@ fn error_on_more_than_one_children_block_in_pane_template() { } "#; let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); - assert!(layout.is_err(), "error provided for more than one children block"); + assert!( + layout.is_err(), + "error provided for more than one children block" + ); } #[test] @@ -809,7 +803,10 @@ fn cannot_define_panes_and_tabs_on_same_level() { } "#; let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()); - assert!(layout.is_err(), "error provided for tab and pane on the same level"); + assert!( + layout.is_err(), + "error provided for tab and pane on the same level" + ); } #[test] @@ -823,10 +820,11 @@ fn cannot_define_tab_template_names_as_keywords() { "command", "plugin", "children", - "tab" + "tab", ]; for keyword in keywords { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ tab_template name=\"{}\" {{ pane @@ -835,9 +833,18 @@ fn cannot_define_tab_template_names_as_keywords() { }} pane }} - ", keyword); + ", + keyword + ); let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into()); - assert!(layout.is_err(), "{}", format!("error provided for tab template name with keyword: {}", keyword)); + assert!( + layout.is_err(), + "{}", + format!( + "error provided for tab template name with keyword: {}", + keyword + ) + ); } } @@ -851,10 +858,11 @@ fn cannot_define_pane_template_names_as_keywords() { "command", "plugin", "children", - "tab" + "tab", ]; for keyword in keywords { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ pane_template name=\"{}\" {{ pane @@ -863,82 +871,104 @@ fn cannot_define_pane_template_names_as_keywords() { }} pane }} - ", keyword); + ", + keyword + ); let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into()); - assert!(layout.is_err(), "{}", format!("error provided for pane template name with keyword: {}", keyword)); + assert!( + layout.is_err(), + "{}", + format!( + "error provided for pane template name with keyword: {}", + keyword + ) + ); } } #[test] fn error_on_multiple_layout_nodes_in_file() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout layout - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_unknown_layout_node() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ pane i_am_not_a_proper_node pane }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_unknown_layout_pane_property() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ pane spit_size=1 }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_unknown_layout_pane_template_property() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ pane_template name=\"my_cool_template\" spit_size=1 }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_unknown_layout_tab_property() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ tab spit_size=1 }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_unknown_layout_tab_template_property() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ tab_template name=\"my_cool_template\" spit_size=1 }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_pane_templates_without_a_name() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ pane_template {{ pane @@ -946,14 +976,16 @@ fn error_on_pane_templates_without_a_name() { pane }} }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } #[test] fn error_on_tab_templates_without_a_name() { - let kdl_layout = format!(" + let kdl_layout = format!( + " layout {{ tab_template {{ pane @@ -961,7 +993,8 @@ fn error_on_tab_templates_without_a_name() { pane }} }} - "); + " + ); let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } diff --git a/zellij-utils/src/input/unit/theme_test.rs b/zellij-utils/src/input/unit/theme_test.rs index df4d6e3d9f..5625920172 100644 --- a/zellij-utils/src/input/unit/theme_test.rs +++ b/zellij-utils/src/input/unit/theme_test.rs @@ -1,6 +1,6 @@ use super::super::theme::*; -use std::path::{Path, PathBuf}; use insta::assert_snapshot; +use std::path::{Path, PathBuf}; fn theme_test_dir(theme: String) -> PathBuf { let root = Path::new(env!("CARGO_MANIFEST_DIR")); diff --git a/zellij-utils/src/ipc.rs b/zellij-utils/src/ipc.rs index 2b7550d38a..9300878865 100644 --- a/zellij-utils/src/ipc.rs +++ b/zellij-utils/src/ipc.rs @@ -3,8 +3,8 @@ use crate::{ cli::CliArgs, data::{ClientId, InputMode, Style}, errors::{get_current_ctx, ErrorContext}, - input::{actions::Action, layout::Layout, options::Options, plugins::PluginsConfig}, input::keybinds::Keybinds, + input::{actions::Action, layout::Layout, options::Options, plugins::PluginsConfig}, pane_size::{Size, SizeInPixels}, }; use interprocess::local_socket::LocalSocketStream; diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 4d7581769f..93e7e8b17f 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -1,87 +1,89 @@ use crate::input::{ command::RunCommand, config::ConfigError, - layout::{Layout, PaneLayout, SplitDirection, Run, RunPlugin, RunPluginLocation, SplitSize}, + layout::{Layout, PaneLayout, Run, RunPlugin, RunPluginLocation, SplitDirection, SplitSize}, }; use kdl::*; -use std::str::FromStr; use std::collections::{HashMap, HashSet}; +use std::str::FromStr; use crate::{ - kdl_parsing_error, + kdl_child_with_name, kdl_children_nodes, kdl_get_bool_property_or_child_value, + kdl_get_bool_property_or_child_value_with_error, kdl_get_child, + kdl_get_int_property_or_child_value, kdl_get_property_or_child, kdl_get_string_entry, + kdl_get_string_property_or_child_value, kdl_get_string_property_or_child_value_with_error, + kdl_name, kdl_parsing_error, kdl_property_names, kdl_property_or_child_value_node, kdl_string_arguments, - kdl_children_nodes, - kdl_name, - kdl_get_string_entry, - kdl_get_child, - kdl_get_bool_property_or_child_value, - kdl_get_bool_property_or_child_value_with_error, - kdl_get_string_property_or_child_value, - kdl_get_string_property_or_child_value_with_error, - kdl_get_property_or_child, - kdl_child_with_name, - kdl_get_int_property_or_child_value, - kdl_property_or_child_value_node, - kdl_property_names, }; use std::convert::TryFrom; -use std::vec::Vec; use std::path::PathBuf; +use std::vec::Vec; use url::Url; -pub struct KdlLayoutParser <'a>{ +pub struct KdlLayoutParser<'a> { raw_layout: &'a str, - file_name: String, - tab_templates: HashMap, - pane_templates: HashMap, - default_tab_template: Option, + tab_templates: HashMap, + pane_templates: HashMap, + default_tab_template: Option<(PaneLayout, KdlNode)>, } -impl <'a>KdlLayoutParser <'a> { - pub fn new(raw_layout: &'a str, file_name: String) -> Self { +impl<'a> KdlLayoutParser<'a> { + pub fn new(raw_layout: &'a str) -> Self { KdlLayoutParser { raw_layout, - file_name, tab_templates: HashMap::new(), pane_templates: HashMap::new(), default_tab_template: None, } } fn is_a_reserved_word(&self, word: &str) -> bool { - word == "pane" || - word == "layout" || - word == "pane_template" || - word == "tab_template" || - word == "default_tab_template" || - word == "command" || - word == "plugin" || - word == "children" || - word == "tab" + word == "pane" + || word == "layout" + || word == "pane_template" + || word == "tab_template" + || word == "default_tab_template" + || word == "command" + || word == "plugin" + || word == "children" + || word == "tab" + || word == "args" + || word == "borderless" + || word == "focus" + || word == "name" + || word == "size" + || word == "cwd" + || word == "split_direction" } fn is_a_valid_pane_property(&self, property_name: &str) -> bool { - property_name == "borderless" || - property_name == "focus" || - property_name == "name" || - property_name == "size" || - property_name == "plugin" || - property_name == "command" || - property_name == "cwd" || - property_name == "args" || - property_name == "split_direction" + property_name == "borderless" + || property_name == "focus" + || property_name == "name" + || property_name == "size" + || property_name == "plugin" + || property_name == "command" + || property_name == "cwd" + || property_name == "args" + || property_name == "split_direction" } fn is_a_valid_tab_property(&self, property_name: &str) -> bool { - property_name == "focus" || - property_name == "name" || - property_name == "split_direction" + property_name == "focus" || property_name == "name" || property_name == "split_direction" } fn assert_legal_node_name(&self, name: &str, kdl_node: &KdlNode) -> Result<(), ConfigError> { if name.contains(char::is_whitespace) { - Err(ConfigError::new_kdl_error(format!("Node names ({}) cannot contain whitespace.", name), kdl_node.span().offset(), kdl_node.span().len())) + Err(ConfigError::new_kdl_error( + format!("Node names ({}) cannot contain whitespace.", name), + kdl_node.span().offset(), + kdl_node.span().len(), + )) } else if self.is_a_reserved_word(&name) { - Err(ConfigError::new_kdl_error(format!("Node name '{}' is a reserved word.", name), kdl_node.span().offset(), kdl_node.span().len())) + Err(ConfigError::new_kdl_error( + format!("Node name '{}' is a reserved word.", name), + kdl_node.span().offset(), + kdl_node.span().len(), + )) } else { Ok(()) } @@ -92,59 +94,103 @@ impl <'a>KdlLayoutParser <'a> { } else if let Some(size) = kdl_get_int_property_or_child_value!(kdl_node, "size") { Ok(Some(SplitSize::Fixed(size as usize))) } else if let Some(node) = kdl_property_or_child_value_node!(kdl_node, "size") { - Err(kdl_parsing_error!(format!("size should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")"), node)) + Err(kdl_parsing_error!( + format!("size should be a fixed number (eg. 1) or a quoted percent (eg. \"50%\")"), + node + )) } else if let Some(node) = kdl_child_with_name!(kdl_node, "size") { - println!("can has child node?"); - Err(kdl_parsing_error!(format!("size cannot be bare, it should have a value (eg. 'size 1', or 'size \"50%\"')"), node)) + Err(kdl_parsing_error!( + format!( + "size cannot be bare, it should have a value (eg. 'size 1', or 'size \"50%\"')" + ), + node + )) } else { Ok(None) } } fn parse_plugin_block(&self, plugin_block: &KdlNode) -> Result, ConfigError> { - let _allow_exec_host_cmd = kdl_get_bool_property_or_child_value_with_error!(plugin_block, "_allow_exec_host_cmd").unwrap_or(false); - let string_url = kdl_get_string_property_or_child_value_with_error!(plugin_block, "location").ok_or(ConfigError::new_kdl_error("Plugins must have a location".into(), plugin_block.span().offset(), plugin_block.span().len()))?; - let url_node = kdl_get_property_or_child!(plugin_block, "location").ok_or(ConfigError::new_kdl_error("Plugins must have a location".into(), plugin_block.span().offset(), plugin_block.span().len()))?; - let url = Url::parse(string_url).map_err(|e| ConfigError::new_kdl_error(format!("Failed to parse url: {:?}", e), url_node.span().offset(), url_node.span().len()))?; + let _allow_exec_host_cmd = + kdl_get_bool_property_or_child_value_with_error!(plugin_block, "_allow_exec_host_cmd") + .unwrap_or(false); + let string_url = + kdl_get_string_property_or_child_value_with_error!(plugin_block, "location").ok_or( + ConfigError::new_kdl_error( + "Plugins must have a location".into(), + plugin_block.span().offset(), + plugin_block.span().len(), + ), + )?; + let url_node = kdl_get_property_or_child!(plugin_block, "location").ok_or( + ConfigError::new_kdl_error( + "Plugins must have a location".into(), + plugin_block.span().offset(), + plugin_block.span().len(), + ), + )?; + let url = Url::parse(string_url).map_err(|e| { + ConfigError::new_kdl_error( + format!("Failed to parse url: {:?}", e), + url_node.span().offset(), + url_node.span().len(), + ) + })?; let location = RunPluginLocation::try_from(url)?; Ok(Some(Run::Plugin(RunPlugin { _allow_exec_host_cmd, - location + location, }))) } fn parse_pane_command(&self, pane_node: &KdlNode) -> Result, ConfigError> { - let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command").map(|c| PathBuf::from(c)); - let cwd = kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd").map(|c| PathBuf::from(c)); + let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command") + .map(|c| PathBuf::from(c)); + let cwd = kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd") + .map(|c| PathBuf::from(c)); let args = match kdl_get_child!(pane_node, "args") { Some(kdl_args) => { if kdl_args.entries().is_empty() { return Err(kdl_parsing_error!(format!("args cannot be empty and should contain one or more command arguments (eg. args \"-h\" \"-v\")"), kdl_args)); } - Some(kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect()) - } + Some( + kdl_string_arguments!(kdl_args) + .iter() + .map(|s| String::from(*s)) + .collect(), + ) + }, None => None, }; match (command, cwd, args) { - (None, Some(cwd), _) => { - Err(ConfigError::new_kdl_error("Cwd can only be set if a command was specified".into(), pane_node.span().offset(), pane_node.span().offset())) - } - (None, _, Some(_args)) => { - Err(ConfigError::new_kdl_error("Args can only be set if a command was specified".into(), pane_node.span().offset(), pane_node.span().len())) - } - (Some(command), cwd, args) => { - Ok(Some(Run::Command(RunCommand { - command, - args: args.unwrap_or_else(|| vec![]), - cwd - }))) - } - _ => Ok(None) + (None, Some(_cwd), _) => Err(ConfigError::new_kdl_error( + "cwd can only be set if a command was specified".into(), + pane_node.span().offset(), + pane_node.span().len(), + )), + (None, _, Some(_args)) => Err(ConfigError::new_kdl_error( + "args can only be set if a command was specified".into(), + pane_node.span().offset(), + pane_node.span().len(), + )), + (Some(command), cwd, args) => Ok(Some(Run::Command(RunCommand { + command, + args: args.unwrap_or_else(|| vec![]), + cwd, + }))), + _ => Ok(None), } } - fn parse_command_or_plugin_block(&self, kdl_node: &KdlNode) -> Result, ConfigError> { + fn parse_command_or_plugin_block( + &self, + kdl_node: &KdlNode, + ) -> Result, ConfigError> { let mut run = self.parse_pane_command(kdl_node)?; if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") { if run.is_some() { - return Err(ConfigError::new_kdl_error("Cannot have both a command and a plugin block for a single pane".into(), plugin_block.span().offset(), plugin_block.span().len())); + return Err(ConfigError::new_kdl_error( + "Cannot have both a command and a plugin block for a single pane".into(), + plugin_block.span().offset(), + plugin_block.span().len(), + )); } run = self.parse_plugin_block(plugin_block)?; } @@ -154,13 +200,14 @@ impl <'a>KdlLayoutParser <'a> { self.assert_valid_pane_properties(kdl_node)?; let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); - let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name").map(|name| name.to_string()); + let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") + .map(|name| name.to_string()); let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_or_plugin_block(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; let (external_children_index, children) = match kdl_children_nodes!(kdl_node) { Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, vec![]) + None => (None, vec![]), }; Ok(PaneLayout { borderless: borderless.unwrap_or_default(), @@ -174,7 +221,19 @@ impl <'a>KdlLayoutParser <'a> { ..Default::default() }) } - fn parse_pane_node_with_template(&self, kdl_node: &KdlNode, mut pane_layout: PaneLayout) -> Result { + fn parse_pane_node_with_template( + &self, + kdl_node: &KdlNode, + mut pane_layout: PaneLayout, + pane_layout_kdl_node: &KdlNode, + ) -> Result { + let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); + let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") + .map(|name| name.to_string()); + let split_size = self.parse_split_size(kdl_node)?; + let run = self.parse_command_or_plugin_block(kdl_node)?; + let children_split_direction = self.parse_split_direction(kdl_node)?; match kdl_children_nodes!(kdl_node) { Some(children) => { @@ -184,14 +243,35 @@ impl <'a>KdlLayoutParser <'a> { children: child_panes, ..Default::default() }; - self.assert_one_children_block(&pane_layout, kdl_node)?; - self.insert_layout_children_or_error(&mut pane_layout, child_panes_layout, kdl_node)?; + self.assert_one_children_block(&pane_layout, pane_layout_kdl_node)?; + self.insert_layout_children_or_error( + &mut pane_layout, + child_panes_layout, + pane_layout_kdl_node, + )?; }, None => { + if let Some(borderless) = borderless { + pane_layout.borderless = borderless; + } + if let Some(focus) = focus { + pane_layout.focus = Some(focus); + } + if let Some(name) = name { + pane_layout.name = Some(name); + } + if let Some(split_size) = split_size { + pane_layout.split_size = Some(split_size); + } + if let Some(run) = run { + pane_layout.run = Some(run); + } if let Some(index_of_children) = pane_layout.external_children_index { - pane_layout.children.insert(index_of_children, PaneLayout::default()); + pane_layout + .children + .insert(index_of_children, PaneLayout::default()); } - } + }, } pane_layout.external_children_index = None; Ok(pane_layout) @@ -204,8 +284,13 @@ impl <'a>KdlLayoutParser <'a> { } fn parse_pane_template_node(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { self.assert_valid_pane_properties(kdl_node)?; - let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()) - .ok_or(ConfigError::new_kdl_error("Pane templates must have a name".into(), kdl_node.span().offset(), kdl_node.span().len()))?; + let template_name = kdl_get_string_property_or_child_value!(kdl_node, "name") + .map(|s| s.to_string()) + .ok_or(ConfigError::new_kdl_error( + "Pane templates must have a name".into(), + kdl_node.span().offset(), + kdl_node.span().len(), + ))?; self.assert_legal_node_name(&template_name, kdl_node)?; let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); @@ -214,23 +299,34 @@ impl <'a>KdlLayoutParser <'a> { let children_split_direction = self.parse_split_direction(kdl_node)?; let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, vec![]) + None => (None, vec![]), }; - self.pane_templates.insert(template_name, PaneLayout { - borderless: borderless.unwrap_or_default(), - focus, - split_size, - run, - children_split_direction, - external_children_index, - children: pane_parts, - ..Default::default() - }); + self.pane_templates.insert( + template_name, + ( + PaneLayout { + borderless: borderless.unwrap_or_default(), + focus, + split_size, + run, + children_split_direction, + external_children_index, + children: pane_parts, + ..Default::default() + }, + kdl_node.clone(), + ), + ); Ok(()) } - fn parse_tab_node(&mut self, kdl_node: &KdlNode) -> Result<(bool, Option, PaneLayout), ConfigError> { // (is_focused, Option, PaneLayout) + fn parse_tab_node( + &mut self, + kdl_node: &KdlNode, + ) -> Result<(bool, Option, PaneLayout), ConfigError> { + // (is_focused, Option, PaneLayout) self.assert_valid_tab_properties(kdl_node)?; - let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let tab_name = + kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, @@ -240,19 +336,32 @@ impl <'a>KdlLayoutParser <'a> { Some(children) => self.parse_child_pane_nodes_for_tab(children)?, None => vec![], }; - Ok((is_focused, tab_name, PaneLayout { - children_split_direction, - children, - ..Default::default() - })) + Ok(( + is_focused, + tab_name, + PaneLayout { + children_split_direction, + children, + ..Default::default() + }, + )) } - fn parse_child_pane_nodes_for_tab(&self, children: &[KdlNode]) -> Result, ConfigError> { + fn parse_child_pane_nodes_for_tab( + &self, + children: &[KdlNode], + ) -> Result, ConfigError> { let mut nodes = vec![]; for child in children { if kdl_name!(child) == "pane" { nodes.push(self.parse_pane_node(child)?); - } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { - nodes.push(self.parse_pane_node_with_template(child, pane_template)?); + } else if let Some((pane_template, pane_template_kdl_node)) = + self.pane_templates.get(kdl_name!(child)).cloned() + { + nodes.push(self.parse_pane_node_with_template( + child, + pane_template, + &pane_template_kdl_node, + )?); } } if nodes.is_empty() { @@ -260,7 +369,11 @@ impl <'a>KdlLayoutParser <'a> { } Ok(nodes) } - fn parse_child_pane_nodes_for_pane(&self, children: &[KdlNode]) -> Result<(Option, Vec), ConfigError> { // usize is external_children_index + fn parse_child_pane_nodes_for_pane( + &self, + children: &[KdlNode], + ) -> Result<(Option, Vec), ConfigError> { + // usize is external_children_index let mut external_children_index = None; let mut nodes = vec![]; for (i, child) in children.iter().enumerate() { @@ -268,16 +381,26 @@ impl <'a>KdlLayoutParser <'a> { nodes.push(self.parse_pane_node(child)?); } else if kdl_name!(child) == "children" { external_children_index = Some(i); - } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { - nodes.push(self.parse_pane_node_with_template(child, pane_template)?); + } else if let Some((pane_template, pane_template_kdl_node)) = + self.pane_templates.get(kdl_name!(child)).cloned() + { + nodes.push(self.parse_pane_node_with_template( + child, + pane_template, + &pane_template_kdl_node, + )?); } } Ok((external_children_index, nodes)) } - fn assert_one_children_block(&self, layout: &PaneLayout, kdl_node: &KdlNode) -> Result<(), ConfigError> { + fn assert_one_children_block( + &self, + layout: &PaneLayout, + kdl_node: &KdlNode, + ) -> Result<(), ConfigError> { let children_block_count = layout.children_block_count(); if children_block_count != 1 { - return Err(ConfigError::new_kdl_error(format!("Layout has {} children blocks, only 1 is allowed", children_block_count), kdl_node.span().offset(), kdl_node.span().len())); + return Err(ConfigError::new_kdl_error(format!("This template has {} children blocks, only 1 is allowed when used to insert child panes", children_block_count), kdl_node.span().offset(), kdl_node.span().len())); } Ok(()) } @@ -285,7 +408,11 @@ impl <'a>KdlLayoutParser <'a> { let all_property_names = kdl_property_names!(pane_node); for name in all_property_names { if !self.is_a_valid_pane_property(name) { - return Err(ConfigError::new_kdl_error(format!("Invalid pane property '{}'", name), pane_node.span().offset(), pane_node.span().len())); + return Err(ConfigError::new_kdl_error( + format!("Invalid pane property '{}'", name), + pane_node.span().offset(), + pane_node.span().len(), + )); } } Ok(()) @@ -294,21 +421,41 @@ impl <'a>KdlLayoutParser <'a> { let all_property_names = kdl_property_names!(pane_node); for name in all_property_names { if !self.is_a_valid_tab_property(name) { - return Err(ConfigError::new_kdl_error(format!("Invalid tab property '{}'", name), pane_node.span().offset(), pane_node.span().len())); + return Err(ConfigError::new_kdl_error( + format!("Invalid tab property '{}'", name), + pane_node.span().offset(), + pane_node.span().len(), + )); } } Ok(()) } - fn insert_layout_children_or_error(&self, layout: &mut PaneLayout, mut child_panes_layout: PaneLayout, kdl_node: &KdlNode) -> Result<(), ConfigError> { + fn insert_layout_children_or_error( + &self, + layout: &mut PaneLayout, + mut child_panes_layout: PaneLayout, + kdl_node: &KdlNode, + ) -> Result<(), ConfigError> { let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?; if !successfully_inserted { - Err(ConfigError::new_kdl_error("This tab template does not have children".into(), kdl_node.span().offset(), kdl_node.span().len())) + Err(ConfigError::new_kdl_error( + "This template does not have children".into(), + kdl_node.span().offset(), + kdl_node.span().len(), + )) } else { Ok(()) } } - fn parse_tab_node_with_template(&mut self, kdl_node: &KdlNode, mut tab_layout: PaneLayout) -> Result<(bool, Option, PaneLayout), ConfigError> { // (is_focused, Option, PaneLayout) - let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + fn parse_tab_node_with_template( + &self, + kdl_node: &KdlNode, + mut tab_layout: PaneLayout, + tab_layout_kdl_node: &KdlNode, + ) -> Result<(bool, Option, PaneLayout), ConfigError> { + // (is_focused, Option, PaneLayout) + let tab_name = + kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); let children_split_direction = match kdl_get_string_entry!(kdl_node, "split_direction") { Some(direction) => SplitDirection::from_str(direction)?, @@ -322,35 +469,51 @@ impl <'a>KdlLayoutParser <'a> { children: child_panes, ..Default::default() }; - self.assert_one_children_block(&tab_layout, kdl_node)?; - self.insert_layout_children_or_error(&mut tab_layout, child_panes_layout, kdl_node)?; + self.assert_one_children_block(&tab_layout, &tab_layout_kdl_node)?; + self.insert_layout_children_or_error( + &mut tab_layout, + child_panes_layout, + &tab_layout_kdl_node, + )?; }, None => { if let Some(index_of_children) = tab_layout.external_children_index { - tab_layout.children.insert(index_of_children, PaneLayout::default()); + tab_layout + .children + .insert(index_of_children, PaneLayout::default()); } - } + }, } tab_layout.external_children_index = None; Ok((is_focused, tab_name, tab_layout)) } fn populate_one_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { - let template_name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name").map(|s| s.to_string()) - .ok_or(ConfigError::new_kdl_error("Tab templates must have a name".into(), kdl_node.span().offset(), kdl_node.span().len()))?; + let template_name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") + .map(|s| s.to_string()) + .ok_or(ConfigError::new_kdl_error( + "Tab templates must have a name".into(), + kdl_node.span().offset(), + kdl_node.span().len(), + ))?; self.assert_legal_node_name(&template_name, kdl_node)?; - self.tab_templates.insert(template_name, self.parse_tab_template_node(kdl_node)?); + self.tab_templates.insert( + template_name, + (self.parse_tab_template_node(kdl_node)?, kdl_node.clone()), + ); Ok(()) } fn populate_default_tab_template(&mut self, kdl_node: &KdlNode) -> Result<(), ConfigError> { - self.default_tab_template = Some(self.parse_tab_template_node(kdl_node)?); + self.default_tab_template = + Some((self.parse_tab_template_node(kdl_node)?, kdl_node.clone())); Ok(()) } fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result { self.assert_valid_tab_properties(kdl_node)?; - let children_split_direction = match kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction") { - Some(direction) => SplitDirection::from_str(direction)?, - None => SplitDirection::default(), - }; + let children_split_direction = + match kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction") { + Some(direction) => SplitDirection::from_str(direction)?, + None => SplitDirection::default(), + }; let mut tab_children = vec![]; let mut external_children_index = None; if let Some(children) = kdl_children_nodes!(kdl_node) { @@ -359,8 +522,14 @@ impl <'a>KdlLayoutParser <'a> { tab_children.push(self.parse_pane_node(child)?); } else if kdl_name!(child) == "children" { external_children_index = Some(i); - } else if let Some(pane_template) = self.pane_templates.get(kdl_name!(child)).cloned() { - tab_children.push(self.parse_pane_node_with_template(child, pane_template)?); + } else if let Some((pane_template, pane_template_kdl_node)) = + self.pane_templates.get(kdl_name!(child)).cloned() + { + tab_children.push(self.parse_pane_node_with_template( + child, + pane_template, + &pane_template_kdl_node, + )?); } } } @@ -373,23 +542,33 @@ impl <'a>KdlLayoutParser <'a> { } fn default_template(&self) -> Result, ConfigError> { match &self.default_tab_template { - Some(template) => { + Some((template, _kdl_node)) => { let mut template = template.clone(); if let Some(children_index) = template.external_children_index { - template.children.insert(children_index, PaneLayout::default()) + template + .children + .insert(children_index, PaneLayout::default()) } template.external_children_index = None; Ok(Some(template)) }, - None => Ok(None) + None => Ok(None), } } - pub fn get_pane_template_dependency_tree(&self, kdl_children: &'a [KdlNode]) -> Result>, ConfigError> { + pub fn get_pane_template_dependency_tree( + &self, + kdl_children: &'a [KdlNode], + ) -> Result>, ConfigError> { let mut dependency_tree = HashMap::new(); for child in kdl_children { if kdl_name!(child) == "pane_template" { - let template_name = kdl_get_string_property_or_child_value!(child, "name") - .ok_or(ConfigError::new_kdl_error("Pane templates must have a name".into(), child.span().offset(), child.span().len()))?; + let template_name = kdl_get_string_property_or_child_value!(child, "name").ok_or( + ConfigError::new_kdl_error( + "Pane templates must have a name".into(), + child.span().offset(), + child.span().len(), + ), + )?; let mut template_children = HashSet::new(); self.get_pane_template_dependencies(child, &mut template_children)?; dependency_tree.insert(template_name, template_children); @@ -397,7 +576,11 @@ impl <'a>KdlLayoutParser <'a> { } Ok(dependency_tree) } - fn get_pane_template_dependencies(&self, node: &'a KdlNode, all_dependencies: &mut HashSet<&'a str>) -> Result<(), ConfigError> { + fn get_pane_template_dependencies( + &self, + node: &'a KdlNode, + all_dependencies: &mut HashSet<&'a str>, + ) -> Result<(), ConfigError> { if let Some(children) = kdl_children_nodes!(node) { for child in children { let child_name = kdl_name!(child); @@ -411,7 +594,11 @@ impl <'a>KdlLayoutParser <'a> { } Ok(()) } - pub fn parse_pane_template_by_name(&mut self, pane_template_name: &str, kdl_children: &[KdlNode]) -> Result<(), ConfigError> { + pub fn parse_pane_template_by_name( + &mut self, + pane_template_name: &str, + kdl_children: &[KdlNode], + ) -> Result<(), ConfigError> { for child in kdl_children.iter() { let child_name = kdl_name!(child); if child_name == "pane_template" { @@ -420,11 +607,16 @@ impl <'a>KdlLayoutParser <'a> { self.parse_pane_template_node(child)?; } } - }; + } Ok(()) } - fn populate_pane_templates(&mut self, layout_children: &[KdlNode], kdl_layout: &KdlDocument) -> Result<(), ConfigError> { - let mut pane_template_dependency_tree = self.get_pane_template_dependency_tree(layout_children)?; + fn populate_pane_templates( + &mut self, + layout_children: &[KdlNode], + kdl_layout: &KdlDocument, + ) -> Result<(), ConfigError> { + let mut pane_template_dependency_tree = + self.get_pane_template_dependency_tree(layout_children)?; let mut pane_template_names_to_parse: Vec<&str> = vec![]; // toposort the dependency tree so that we parse the pane_templates before their // dependencies @@ -436,7 +628,11 @@ impl <'a>KdlLayoutParser <'a> { } } if candidates.is_empty() { - return Err(ConfigError::new_kdl_error("Circular dependency detected between pane templates.".into(), kdl_layout.span().offset(), kdl_layout.span().len())); + return Err(ConfigError::new_kdl_error( + "Circular dependency detected between pane templates.".into(), + kdl_layout.span().offset(), + kdl_layout.span().len(), + )); } for candidate_to_remove in candidates { pane_template_dependency_tree.remove(candidate_to_remove); @@ -463,8 +659,14 @@ impl <'a>KdlLayoutParser <'a> { } Ok(()) } - fn layout_with_tabs(&self, tabs: Vec<(Option, PaneLayout)>, focused_tab_index: Option) -> Result { - let template = self.default_template()?.unwrap_or_else(|| PaneLayout::default()); + fn layout_with_tabs( + &self, + tabs: Vec<(Option, PaneLayout)>, + focused_tab_index: Option, + ) -> Result { + let template = self + .default_template()? + .unwrap_or_else(|| PaneLayout::default()); Ok(Layout { tabs: tabs, @@ -495,53 +697,113 @@ impl <'a>KdlLayoutParser <'a> { }) } fn layout_with_one_pane(&self) -> Result { - let template = self.default_template()?.unwrap_or_else(|| PaneLayout::default()); + let template = self + .default_template()? + .unwrap_or_else(|| PaneLayout::default()); Ok(Layout { template: Some(template), ..Default::default() }) } - fn populate_layout_child(&mut self, child: &KdlNode, child_tabs: &mut Vec<(bool, Option, PaneLayout)>, child_panes: &mut Vec) -> Result<(), ConfigError> { + fn populate_layout_child( + &mut self, + child: &KdlNode, + child_tabs: &mut Vec<(bool, Option, PaneLayout)>, + child_panes: &mut Vec, + ) -> Result<(), ConfigError> { let child_name = kdl_name!(child); if child_name == "pane" { if !child_tabs.is_empty() { - return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); + return Err(ConfigError::new_kdl_error( + "Cannot have both tabs and panes in the same node".into(), + child.span().offset(), + child.span().len(), + )); } child_panes.push(self.parse_pane_node(child)?); } else if child_name == "tab" { if !child_panes.is_empty() { - return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); + return Err(ConfigError::new_kdl_error( + "Cannot have both tabs and panes in the same node".into(), + child.span().offset(), + child.span().len(), + )); } match &self.default_tab_template { - Some(default_tab_template) => { + Some((default_tab_template, default_tab_template_kdl_node)) => { let default_tab_template = default_tab_template.clone(); - child_tabs.push(self.parse_tab_node_with_template(child, default_tab_template)?); + child_tabs.push(self.parse_tab_node_with_template( + child, + default_tab_template, + default_tab_template_kdl_node, + )?); }, None => { child_tabs.push(self.parse_tab_node(child)?); - } + }, } - } else if let Some(tab_template) = self.tab_templates.get(child_name).cloned() { + } else if let Some((tab_template, tab_template_kdl_node)) = + self.tab_templates.get(child_name).cloned() + { if !child_panes.is_empty() { - return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); + return Err(ConfigError::new_kdl_error( + "Cannot have both tabs and panes in the same node".into(), + child.span().offset(), + child.span().len(), + )); } - child_tabs.push(self.parse_tab_node_with_template(child, tab_template)?); - } else if let Some(pane_template) = self.pane_templates.get(child_name).cloned() { + child_tabs.push(self.parse_tab_node_with_template( + child, + tab_template, + &tab_template_kdl_node, + )?); + } else if let Some((pane_template, pane_template_kdl_node)) = + self.pane_templates.get(child_name).cloned() + { if !child_tabs.is_empty() { - return Err(ConfigError::new_kdl_error("Cannot have both tabs and panes in the same node".into(), child.span().offset(), child.span().len())); + return Err(ConfigError::new_kdl_error( + "Cannot have both tabs and panes in the same node".into(), + child.span().offset(), + child.span().len(), + )); } - child_panes.push(self.parse_pane_node_with_template(child, pane_template)?); + child_panes.push(self.parse_pane_node_with_template( + child, + pane_template, + &pane_template_kdl_node, + )?); } else if !self.is_a_reserved_word(child_name) { - return Err(ConfigError::new_kdl_error(format!("Unknown layout node: '{}'", child_name), child.span().offset(), child.span().len())); + return Err(ConfigError::new_kdl_error( + format!("Unknown layout node: '{}'", child_name), + child.span().offset(), + child.span().len(), + )); } Ok(()) } pub fn parse(&mut self) -> Result { let kdl_layout: KdlDocument = self.raw_layout.parse()?; - let layout_node = kdl_layout.nodes().iter().find(|n| kdl_name!(n) == "layout").ok_or(ConfigError::new_kdl_error("No layout found".into(), kdl_layout.span().offset(), kdl_layout.span().len()))?; - let has_multiple_layout_nodes = kdl_layout.nodes().iter().filter(|n| kdl_name!(n) == "layout").count() > 1; + let layout_node = kdl_layout + .nodes() + .iter() + .find(|n| kdl_name!(n) == "layout") + .ok_or(ConfigError::new_kdl_error( + "No layout found".into(), + kdl_layout.span().offset(), + kdl_layout.span().len(), + ))?; + let has_multiple_layout_nodes = kdl_layout + .nodes() + .iter() + .filter(|n| kdl_name!(n) == "layout") + .count() + > 1; if has_multiple_layout_nodes { - return Err(ConfigError::new_kdl_error("Only one layout node per file allowed".into(), kdl_layout.span().offset(), kdl_layout.span().len())); + return Err(ConfigError::new_kdl_error( + "Only one layout node per file allowed".into(), + kdl_layout.span().offset(), + kdl_layout.span().len(), + )); } let mut child_tabs = vec![]; let mut child_panes = vec![]; @@ -553,12 +815,23 @@ impl <'a>KdlLayoutParser <'a> { } } if !child_tabs.is_empty() { - let has_more_than_one_focused_tab = child_tabs.iter().filter(|(is_focused, _, _)| *is_focused).count() > 1; + let has_more_than_one_focused_tab = child_tabs + .iter() + .filter(|(is_focused, _, _)| *is_focused) + .count() + > 1; if has_more_than_one_focused_tab { - return Err(ConfigError::new_kdl_error("Only one tab can be focused".into(), kdl_layout.span().offset(), kdl_layout.span().len())); + return Err(ConfigError::new_kdl_error( + "Only one tab can be focused".into(), + kdl_layout.span().offset(), + kdl_layout.span().len(), + )); } let focused_tab_index = child_tabs.iter().position(|(is_focused, _, _)| *is_focused); - let child_tabs: Vec<(Option, PaneLayout)> = child_tabs.drain(..).map(|(_is_focused, tab_name, pane_layout)| (tab_name, pane_layout)).collect(); + let child_tabs: Vec<(Option, PaneLayout)> = child_tabs + .drain(..) + .map(|(_is_focused, tab_name, pane_layout)| (tab_name, pane_layout)) + .collect(); self.layout_with_tabs(child_tabs, focused_tab_index) } else if !child_panes.is_empty() { self.layout_with_one_tab(child_panes) diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index ada4629aa4..8743412e94 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,72 +1,78 @@ mod kdl_layout_parser; -use kdl_layout_parser::KdlLayoutParser; -use strum::IntoEnumIterator; +use crate::data::{InputMode, Key, Palette, PaletteColor}; use crate::envs::EnvironmentVariables; use crate::input::command::RunCommand; +use crate::input::config::{Config, ConfigError, KdlError}; use crate::input::keybinds::Keybinds; use crate::input::layout::{Layout, RunPlugin, RunPluginLocation}; -use crate::input::config::{Config, ConfigError, KdlError}; -use url::Url; -use crate::data::{InputMode, Key, CharOrArrow, PaletteColor, Palette}; -use crate::input::options::{Options, OnForceClose, Clipboard}; +use crate::input::options::{Clipboard, OnForceClose, Options}; +use crate::input::plugins::{PluginConfig, PluginTag, PluginType, PluginsConfig}; +use crate::input::theme::{FrameConfig, Theme, Themes, UiConfig}; +use kdl_layout_parser::KdlLayoutParser; use std::collections::HashMap; use std::fs::File; use std::io::Read; -use crate::input::plugins::{PluginsConfig, PluginConfig, PluginType, PluginTag}; -use crate::input::theme::{UiConfig, Theme, Themes, FrameConfig}; +use strum::IntoEnumIterator; +use url::Url; use miette::NamedSource; -use kdl::{KdlDocument, KdlValue, KdlNode, KdlEntry}; +use kdl::{KdlDocument, KdlEntry, KdlNode}; -use std::str::FromStr; use std::path::PathBuf; +use std::str::FromStr; -use crate::input::actions::{Action, ResizeDirection, Direction, SearchOption, SearchDirection}; +use crate::input::actions::{Action, Direction, ResizeDirection, SearchDirection, SearchOption}; use crate::input::command::RunCommandAction; #[macro_export] macro_rules! parse_kdl_action_arguments { - ( $action_name:expr, $action_arguments:expr, $action_node:expr ) => { - { - if !$action_arguments.is_empty() { - Err(ConfigError::new_kdl_error(format!("Action '{}' must have arguments", $action_name), $action_node.span().offset(), $action_node.span().len())) - } else { - match $action_name { - "Quit" => Ok(Action::Quit), - "FocusNextPane" => Ok(Action::FocusNextPane), - "FocusPreviousPane" => Ok(Action::FocusPreviousPane), - "SwitchFocus" => Ok(Action::SwitchFocus), - "EditScrollback" => Ok(Action::EditScrollback), - "ScrollUp" => Ok(Action::ScrollUp), - "ScrollDown" => Ok(Action::ScrollDown), - "ScrollToBottom" => Ok(Action::ScrollToBottom), - "PageScrollUp" => Ok(Action::PageScrollUp), - "PageScrollDown" => Ok(Action::PageScrollDown), - "HalfPageScrollUp" => Ok(Action::HalfPageScrollUp), - "HalfPageScrollDown" => Ok(Action::HalfPageScrollDown), - "ToggleFocusFullscreen" => Ok(Action::ToggleFocusFullscreen), - "TogglePaneFrames" => Ok(Action::TogglePaneFrames), - "ToggleActiveSyncTab" => Ok(Action::ToggleActiveSyncTab), - "TogglePaneEmbedOrFloating" => Ok(Action::TogglePaneEmbedOrFloating), - "ToggleFloatingPanes" => Ok(Action::ToggleFloatingPanes), - "CloseFocus" => Ok(Action::CloseFocus), - "UndoRenamePane" => Ok(Action::UndoRenamePane), - "NoOp" => Ok(Action::NoOp), - "GoToNextTab" => Ok(Action::GoToNextTab), - "GoToPreviousTab" => Ok(Action::GoToPreviousTab), - "CloseTab" => Ok(Action::CloseTab), - "ToggleTab" => Ok(Action::ToggleTab), - "UndoRenameTab" => Ok(Action::UndoRenameTab), - "Detach" => Ok(Action::Detach), - "Copy" => Ok(Action::Copy), - "Confirm" => Ok(Action::Confirm), - "Deny" => Ok(Action::Deny), - _ => Err(ConfigError::new_kdl_error(format!("Unsupported action: {:?}", $action_name), $action_node.span().offset(), $action_node.span().len())) - } + ( $action_name:expr, $action_arguments:expr, $action_node:expr ) => {{ + if !$action_arguments.is_empty() { + Err(ConfigError::new_kdl_error( + format!("Action '{}' must have arguments", $action_name), + $action_node.span().offset(), + $action_node.span().len(), + )) + } else { + match $action_name { + "Quit" => Ok(Action::Quit), + "FocusNextPane" => Ok(Action::FocusNextPane), + "FocusPreviousPane" => Ok(Action::FocusPreviousPane), + "SwitchFocus" => Ok(Action::SwitchFocus), + "EditScrollback" => Ok(Action::EditScrollback), + "ScrollUp" => Ok(Action::ScrollUp), + "ScrollDown" => Ok(Action::ScrollDown), + "ScrollToBottom" => Ok(Action::ScrollToBottom), + "PageScrollUp" => Ok(Action::PageScrollUp), + "PageScrollDown" => Ok(Action::PageScrollDown), + "HalfPageScrollUp" => Ok(Action::HalfPageScrollUp), + "HalfPageScrollDown" => Ok(Action::HalfPageScrollDown), + "ToggleFocusFullscreen" => Ok(Action::ToggleFocusFullscreen), + "TogglePaneFrames" => Ok(Action::TogglePaneFrames), + "ToggleActiveSyncTab" => Ok(Action::ToggleActiveSyncTab), + "TogglePaneEmbedOrFloating" => Ok(Action::TogglePaneEmbedOrFloating), + "ToggleFloatingPanes" => Ok(Action::ToggleFloatingPanes), + "CloseFocus" => Ok(Action::CloseFocus), + "UndoRenamePane" => Ok(Action::UndoRenamePane), + "NoOp" => Ok(Action::NoOp), + "GoToNextTab" => Ok(Action::GoToNextTab), + "GoToPreviousTab" => Ok(Action::GoToPreviousTab), + "CloseTab" => Ok(Action::CloseTab), + "ToggleTab" => Ok(Action::ToggleTab), + "UndoRenameTab" => Ok(Action::UndoRenameTab), + "Detach" => Ok(Action::Detach), + "Copy" => Ok(Action::Copy), + "Confirm" => Ok(Action::Confirm), + "Deny" => Ok(Action::Deny), + _ => Err(ConfigError::new_kdl_error( + format!("Unsupported action: {:?}", $action_name), + $action_node.span().offset(), + $action_node.span().len(), + )), } } - }; + }}; } #[macro_export] @@ -77,52 +83,62 @@ macro_rules! parse_kdl_action_u8_arguments { match kdl_entry.value().as_i64() { Some(int_value) => bytes.push(int_value as u8), None => { - return Err(ConfigError::new_kdl_error(format!("Arguments for '{}' must be integers", $action_name), kdl_entry.span().offset(), kdl_entry.span().len())); - } + return Err(ConfigError::new_kdl_error( + format!("Arguments for '{}' must be integers", $action_name), + kdl_entry.span().offset(), + kdl_entry.span().len(), + )); + }, } - }; + } Action::new_from_bytes($action_name, bytes, $action_node) - }} + }}; } - #[macro_export] macro_rules! kdl_parsing_error { ( $message:expr, $entry:expr ) => { - ConfigError::new_kdl_error( - $message, - $entry.span().offset(), - $entry.span().len() - ) - } + ConfigError::new_kdl_error($message, $entry.span().offset(), $entry.span().len()) + }; } #[macro_export] macro_rules! kdl_entries_as_i64 { ( $node:expr ) => { - $node.entries().iter().map(|kdl_node| kdl_node.value().as_i64()) - } + $node + .entries() + .iter() + .map(|kdl_node| kdl_node.value().as_i64()) + }; } #[macro_export] macro_rules! kdl_first_entry_as_string { ( $node:expr ) => { - $node.entries().iter().next().and_then(|s| s.value().as_string()) - } + $node + .entries() + .iter() + .next() + .and_then(|s| s.value().as_string()) + }; } #[macro_export] macro_rules! kdl_first_entry_as_i64 { ( $node:expr ) => { - $node.entries().iter().next().and_then(|i| i.value().as_i64()) - } + $node + .entries() + .iter() + .next() + .and_then(|i| i.value().as_i64()) + }; } #[macro_export] macro_rules! entry_count { ( $node:expr ) => {{ $node.entries().iter().len() - }} + }}; } #[macro_export] @@ -133,74 +149,103 @@ macro_rules! parse_kdl_action_char_or_string_arguments { match kdl_entry.value().as_string() { Some(string_value) => chars_to_write.push_str(string_value), None => { - return Err(ConfigError::new_kdl_error(format!("All entries for action '{}' must be strings", $action_name), kdl_entry.span().offset(), kdl_entry.span().len())) - } + return Err(ConfigError::new_kdl_error( + format!("All entries for action '{}' must be strings", $action_name), + kdl_entry.span().offset(), + kdl_entry.span().len(), + )) + }, } - }; + } Action::new_from_string($action_name, chars_to_write, $action_node) - }} + }}; } #[macro_export] macro_rules! kdl_arg_is_truthy { ( $kdl_node:expr, $arg_name:expr ) => { match $kdl_node.get($arg_name) { - Some(arg) => { - match arg.value().as_bool() { - Some(value) => value, - None => { - return Err(ConfigError::new_kdl_error(format!("Argument must be true or false, found: {}", arg.value()), arg.span().offset(), arg.span().len())) - } - } + Some(arg) => match arg.value().as_bool() { + Some(value) => value, + None => { + return Err(ConfigError::new_kdl_error( + format!("Argument must be true or false, found: {}", arg.value()), + arg.span().offset(), + arg.span().len(), + )) + }, }, - None => { - false - } + None => false, } - } + }; } #[macro_export] macro_rules! kdl_children_nodes_or_error { ( $kdl_node:expr, $error:expr ) => { - $kdl_node.children().ok_or(ConfigError::new_kdl_error($error.into(), $kdl_node.span().offset(), $kdl_node.span().len()))?.nodes() - } + $kdl_node + .children() + .ok_or(ConfigError::new_kdl_error( + $error.into(), + $kdl_node.span().offset(), + $kdl_node.span().len(), + ))? + .nodes() + }; } #[macro_export] macro_rules! kdl_children_nodes { ( $kdl_node:expr ) => { $kdl_node.children().map(|c| c.nodes()) - } + }; } #[macro_export] macro_rules! kdl_children_or_error { ( $kdl_node:expr, $error:expr ) => { - $kdl_node.children().ok_or(ConfigError::new_kdl_error($error.into(), $kdl_node.span().offset(), $kdl_node.span().len()))? - } + $kdl_node.children().ok_or(ConfigError::new_kdl_error( + $error.into(), + $kdl_node.span().offset(), + $kdl_node.span().len(), + ))? + }; } #[macro_export] macro_rules! kdl_children { ( $kdl_node:expr ) => { $kdl_node.children().iter().copied().collect() - } + }; } #[macro_export] macro_rules! kdl_string_arguments { ( $kdl_node:expr ) => {{ - let res: Result, _> = $kdl_node.entries().iter().map(|e| e.value().as_string().ok_or(ConfigError::new_kdl_error("Not a string".into(), e.span().offset(), e.span().len()))).collect(); + let res: Result, _> = $kdl_node + .entries() + .iter() + .map(|e| { + e.value().as_string().ok_or(ConfigError::new_kdl_error( + "Not a string".into(), + e.span().offset(), + e.span().len(), + )) + }) + .collect(); res? - }} + }}; } #[macro_export] macro_rules! kdl_property_names { ( $kdl_node:expr ) => {{ - $kdl_node.entries().iter().filter_map(|e| e.name()).map(|e| e.value()) - }} + $kdl_node + .entries() + .iter() + .filter_map(|e| e.name()) + .map(|e| e.value()) + }}; } #[macro_export] @@ -208,21 +253,21 @@ macro_rules! kdl_argument_values { ( $kdl_node:expr ) => { $kdl_node.entries().iter().collect() // $kdl_node.entries().iter().map(|arg| arg.value()).collect() - } + }; } #[macro_export] macro_rules! kdl_name { ( $kdl_node:expr ) => { $kdl_node.name().value() - } + }; } #[macro_export] macro_rules! kdl_document_name { ( $kdl_node:expr ) => { $kdl_node.node().name().value() - } + }; } #[macro_export] @@ -230,9 +275,17 @@ macro_rules! keys_from_kdl { ( $kdl_node:expr ) => { kdl_string_arguments!($kdl_node) .iter() - .map(|k| Key::from_str(k).map_err(|_| ConfigError::new_kdl_error(format!("Invalid key: '{}'", k), $kdl_node.span().offset(), $kdl_node.span().len()))) + .map(|k| { + Key::from_str(k).map_err(|_| { + ConfigError::new_kdl_error( + format!("Invalid key: '{}'", k), + $kdl_node.span().offset(), + $kdl_node.span().len(), + ) + }) + }) .collect::>()? - } + }; } #[macro_export] @@ -242,25 +295,33 @@ macro_rules! actions_from_kdl { .iter() .map(|kdl_action| Action::try_from(kdl_action)) .collect::>()? - } + }; } - -pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, ConfigError> { -// pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, ConfigError> { +pub fn kdl_arguments_that_are_strings<'a>( + arguments: impl Iterator, +) -> Result, ConfigError> { + // pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator) -> Result, ConfigError> { let mut args: Vec = vec![]; for kdl_entry in arguments { match kdl_entry.value().as_string() { Some(string_value) => args.push(string_value.to_string()), None => { - return Err(ConfigError::new_kdl_error(format!("Argument must be a string"), kdl_entry.span().offset(), kdl_entry.span().len())); - } + return Err(ConfigError::new_kdl_error( + format!("Argument must be a string"), + kdl_entry.span().offset(), + kdl_entry.span().len(), + )); + }, } } Ok(args) } -pub fn kdl_child_string_value_for_entry <'a>(command_metadata: &'a KdlDocument, entry_name: &'a str) -> Option<&'a str> { +pub fn kdl_child_string_value_for_entry<'a>( + command_metadata: &'a KdlDocument, + entry_name: &'a str, +) -> Option<&'a str> { command_metadata .get(entry_name) .and_then(|cwd| cwd.entries().iter().next()) @@ -268,93 +329,135 @@ pub fn kdl_child_string_value_for_entry <'a>(command_metadata: &'a KdlDocument, } impl Action { - pub fn new_from_bytes(action_name: &str, bytes: Vec, action_node: &KdlNode) -> Result { + pub fn new_from_bytes( + action_name: &str, + bytes: Vec, + action_node: &KdlNode, + ) -> Result { match action_name { - "Write" => { - Ok(Action::Write(bytes)) - }, - "PaneNameInput" => { - Ok(Action::PaneNameInput(bytes)) - } - "TabNameInput" => { - Ok(Action::TabNameInput(bytes)) - } - "SearchInput" => { - Ok(Action::SearchInput(bytes)) - } + "Write" => Ok(Action::Write(bytes)), + "PaneNameInput" => Ok(Action::PaneNameInput(bytes)), + "TabNameInput" => Ok(Action::TabNameInput(bytes)), + "SearchInput" => Ok(Action::SearchInput(bytes)), "GoToTab" => { - let tab_index = *bytes - .get(0) - .ok_or_else(|| ConfigError::new_kdl_error(format!("Missing tab index"), action_node.span().offset(), action_node.span().len()))? - as u32; + let tab_index = *bytes.get(0).ok_or_else(|| { + ConfigError::new_kdl_error( + format!("Missing tab index"), + action_node.span().offset(), + action_node.span().len(), + ) + })? as u32; Ok(Action::GoToTab(tab_index)) - } - _ => Err(ConfigError::new_kdl_error("Failed to parse action".into(), action_node.span().offset(), action_node.span().len())) + }, + _ => Err(ConfigError::new_kdl_error( + "Failed to parse action".into(), + action_node.span().offset(), + action_node.span().len(), + )), } } - pub fn new_from_string(action_name: &str, string: String, action_node: &KdlNode) -> Result { + pub fn new_from_string( + action_name: &str, + string: String, + action_node: &KdlNode, + ) -> Result { match action_name { "WriteChars" => Ok(Action::WriteChars(string)), - "SwitchToMode" => { - match InputMode::from_str(string.as_str()) { - Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)), - Err(_e) => return Err(ConfigError::new_kdl_error(format!("Unknown InputMode '{}'", string), action_node.span().offset(), action_node.span().len())) - } + "SwitchToMode" => match InputMode::from_str(string.as_str()) { + Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)), + Err(_e) => { + return Err(ConfigError::new_kdl_error( + format!("Unknown InputMode '{}'", string), + action_node.span().offset(), + action_node.span().len(), + )) + }, }, "Resize" => { let direction = ResizeDirection::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) })?; Ok(Action::Resize(direction)) - } + }, "MoveFocus" => { let direction = Direction::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) })?; Ok(Action::MoveFocus(direction)) - } + }, "MoveFocusOrTab" => { let direction = Direction::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) })?; Ok(Action::MoveFocusOrTab(direction)) - } + }, "MovePane" => { if string.is_empty() { return Ok(Action::MovePane(None)); } else { let direction = Direction::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) })?; Ok(Action::MovePane(Some(direction))) } - } - "DumpScreen" => { - Ok(Action::DumpScreen(string)) - } + }, + "DumpScreen" => Ok(Action::DumpScreen(string)), "NewPane" => { if string.is_empty() { return Ok(Action::NewPane(None)); } else { let direction = Direction::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) })?; Ok(Action::NewPane(Some(direction))) } - } + }, "SearchToggleOption" => { let toggle_option = SearchOption::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) })?; Ok(Action::SearchToggleOption(toggle_option)) - } + }, "Search" => { - let search_direction = SearchDirection::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error(format!("Invalid direction: '{}'", string), action_node.span().offset(), action_node.span().len()) - })?; + let search_direction = + SearchDirection::from_str(string.as_str()).map_err(|_| { + ConfigError::new_kdl_error( + format!("Invalid direction: '{}'", string), + action_node.span().offset(), + action_node.span().len(), + ) + })?; Ok(Action::Search(search_direction)) - } - _ => Err(ConfigError::new_kdl_error(format!("Unsupported action: {}", action_name), action_node.span().offset(), action_node.span().len())) + }, + _ => Err(ConfigError::new_kdl_error( + format!("Unsupported action: {}", action_name), + action_node.span().offset(), + action_node.span().len(), + )), } } } @@ -362,15 +465,23 @@ impl Action { impl TryFrom<(&str, &KdlDocument)> for PaletteColor { type Error = ConfigError; - fn try_from((color_name, theme_colors): (&str, &KdlDocument)) -> Result { - let color = theme_colors.get(color_name).ok_or(ConfigError::new_kdl_error(format!("Missing theme color: {}", color_name), theme_colors.span().offset(), theme_colors.span().len()))?; + fn try_from( + (color_name, theme_colors): (&str, &KdlDocument), + ) -> Result { + let color = theme_colors + .get(color_name) + .ok_or(ConfigError::new_kdl_error( + format!("Missing theme color: {}", color_name), + theme_colors.span().offset(), + theme_colors.span().len(), + ))?; let entry_count = entry_count!(color); let is_rgb = || entry_count == 3; let is_three_digit_hex = || { match kdl_first_entry_as_string!(color) { // 4 including the '#' character Some(s) => entry_count == 1 && s.starts_with('#') && s.len() == 4, - None => false + None => false, } }; let is_six_digit_hex = || { @@ -380,36 +491,90 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor { None => false, } }; - let is_eight_bit = || { - kdl_first_entry_as_i64!(color).is_some() && entry_count == 1 - }; + let is_eight_bit = || kdl_first_entry_as_i64!(color).is_some() && entry_count == 1; if is_rgb() { let mut channels = kdl_entries_as_i64!(color); - let r = channels.next().unwrap().ok_or(ConfigError::new_kdl_error(format!("invalid rgb color"), color.span().offset(), color.span().len()))? as u8; - let g = channels.next().unwrap().ok_or(ConfigError::new_kdl_error(format!("invalid rgb color"), color.span().offset(), color.span().len()))? as u8; - let b = channels.next().unwrap().ok_or(ConfigError::new_kdl_error(format!("invalid rgb color"), color.span().offset(), color.span().len()))? as u8; + let r = channels.next().unwrap().ok_or(ConfigError::new_kdl_error( + format!("invalid rgb color"), + color.span().offset(), + color.span().len(), + ))? as u8; + let g = channels.next().unwrap().ok_or(ConfigError::new_kdl_error( + format!("invalid rgb color"), + color.span().offset(), + color.span().len(), + ))? as u8; + let b = channels.next().unwrap().ok_or(ConfigError::new_kdl_error( + format!("invalid rgb color"), + color.span().offset(), + color.span().len(), + ))? as u8; Ok(PaletteColor::Rgb((r, g, b))) } else if is_three_digit_hex() { // eg. #fff (hex, will be converted to rgb) let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); s.remove(0); - let r = u8::from_str_radix(&s[0..1], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))? * 0x11; - let g = u8::from_str_radix(&s[1..2], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))? * 0x11; - let b = u8::from_str_radix(&s[2..3], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))? * 0x11; + let r = u8::from_str_radix(&s[0..1], 16).map_err(|_| { + ConfigError::new_kdl_error( + "Failed to parse hex color".into(), + color.span().offset(), + color.span().len(), + ) + })? * 0x11; + let g = u8::from_str_radix(&s[1..2], 16).map_err(|_| { + ConfigError::new_kdl_error( + "Failed to parse hex color".into(), + color.span().offset(), + color.span().len(), + ) + })? * 0x11; + let b = u8::from_str_radix(&s[2..3], 16).map_err(|_| { + ConfigError::new_kdl_error( + "Failed to parse hex color".into(), + color.span().offset(), + color.span().len(), + ) + })? * 0x11; Ok(PaletteColor::Rgb((r, g, b))) } else if is_six_digit_hex() { // eg. #ffffff (hex, will be converted to rgb) let mut s = String::from(kdl_first_entry_as_string!(color).unwrap()); s.remove(0); - let r = u8::from_str_radix(&s[0..2], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))?; - let g = u8::from_str_radix(&s[2..4], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))?; - let b = u8::from_str_radix(&s[4..6], 16).map_err(|_| ConfigError::new_kdl_error("Failed to parse hex color".into(), color.span().offset(), color.span().len()))?; + let r = u8::from_str_radix(&s[0..2], 16).map_err(|_| { + ConfigError::new_kdl_error( + "Failed to parse hex color".into(), + color.span().offset(), + color.span().len(), + ) + })?; + let g = u8::from_str_radix(&s[2..4], 16).map_err(|_| { + ConfigError::new_kdl_error( + "Failed to parse hex color".into(), + color.span().offset(), + color.span().len(), + ) + })?; + let b = u8::from_str_radix(&s[4..6], 16).map_err(|_| { + ConfigError::new_kdl_error( + "Failed to parse hex color".into(), + color.span().offset(), + color.span().len(), + ) + })?; Ok(PaletteColor::Rgb((r, g, b))) } else if is_eight_bit() { - let n = kdl_first_entry_as_i64!(color).ok_or(ConfigError::new_kdl_error("Failed to parse color".into(), color.span().offset(), color.span().len()))?; + let n = kdl_first_entry_as_i64!(color).ok_or(ConfigError::new_kdl_error( + "Failed to parse color".into(), + color.span().offset(), + color.span().len(), + ))?; Ok(PaletteColor::EightBit(n as u8)) } else { - Err(ConfigError::new_kdl_error("Failed to parse color".into(), color.span().offset(), color.span().len())) + Err(ConfigError::new_kdl_error( + "Failed to parse color".into(), + color.span().offset(), + color.span().len(), + )) } } } @@ -418,62 +583,143 @@ impl TryFrom<&KdlNode> for Action { // type Error = Box; type Error = ConfigError; fn try_from(kdl_action: &KdlNode) -> Result { - let action_name = kdl_name!(kdl_action); let action_arguments: Vec<&KdlEntry> = kdl_argument_values!(kdl_action); // let action_arguments: Vec<&KdlValue> = kdl_argument_values!(kdl_action); let action_children: Vec<&KdlDocument> = kdl_children!(kdl_action); match action_name { "Quit" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "FocusNextPane" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "FocusPreviousPane" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "FocusNextPane" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "FocusPreviousPane" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "SwitchFocus" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "EditScrollback" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "EditScrollback" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "ScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "ScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "ScrollToBottom" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "PageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "PageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "HalfPageScrollUp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "HalfPageScrollDown" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "ToggleFocusFullscreen" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "TogglePaneFrames" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "ToggleActiveSyncTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "TogglePaneEmbedOrFloating" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "ToggleFloatingPanes" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "ScrollToBottom" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "PageScrollUp" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "PageScrollDown" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "HalfPageScrollUp" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "HalfPageScrollDown" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "ToggleFocusFullscreen" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "TogglePaneFrames" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "ToggleActiveSyncTab" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "TogglePaneEmbedOrFloating" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, + "ToggleFloatingPanes" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "CloseFocus" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "UndoRenamePane" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "UndoRenamePane" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "NoOp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "GoToNextTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "GoToPreviousTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "GoToPreviousTab" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "CloseTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "ToggleTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), - "UndoRenameTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), + "UndoRenameTab" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "Detach" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "Copy" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "Confirm" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "Deny" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), - "WriteChars" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "SwitchToMode" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "Search" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "Resize" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "MoveFocus" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "MoveFocusOrTab" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "MovePane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "DumpScreen" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "NewPane" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), - "PaneNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), + "WriteChars" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "SwitchToMode" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "Search" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "Resize" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "MoveFocus" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "MoveFocusOrTab" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "MovePane" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "DumpScreen" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "NewPane" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), + "PaneNameInput" => { + parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action) + }, "NewTab" => Ok(Action::NewTab(None, None)), "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), - "TabNameInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), - "SearchInput" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), - "SearchToggleOption" => parse_kdl_action_char_or_string_arguments!(action_name, action_arguments, kdl_action), + "TabNameInput" => { + parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action) + }, + "SearchInput" => { + parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action) + }, + "SearchToggleOption" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), "Run" => { let arguments = action_arguments.iter().copied(); let mut args = kdl_arguments_that_are_strings(arguments)?; if args.is_empty() { - return Err(ConfigError::new_kdl_error("No command found in Run action".into(), kdl_action.span().offset(), kdl_action.span().len())); + return Err(ConfigError::new_kdl_error( + "No command found in Run action".into(), + kdl_action.span().offset(), + kdl_action.span().len(), + )); } let command = args.remove(0); let command_metadata = action_children.iter().next(); @@ -490,10 +736,12 @@ impl TryFrom<&KdlNode> for Action { direction, }; Ok(Action::Run(run_command_action)) - } - _ => { - Err(ConfigError::new_kdl_error(format!("Unsupported action: {}", action_name).into(), kdl_action.span().offset(), kdl_action.span().len())) - } + }, + _ => Err(ConfigError::new_kdl_error( + format!("Unsupported action: {}", action_name).into(), + kdl_action.span().offset(), + kdl_action.span().len(), + )), } } } @@ -501,384 +749,465 @@ impl TryFrom<&KdlNode> for Action { #[macro_export] macro_rules! kdl_property_first_arg_as_string { ( $kdl_node:expr, $property_name:expr ) => { - $kdl_node.get($property_name) + $kdl_node + .get($property_name) .and_then(|p| p.entries().iter().next()) .and_then(|p| p.value().as_string()) - } + }; } #[macro_export] macro_rules! kdl_property_first_arg_as_string_or_error { ( $kdl_node:expr, $property_name:expr ) => {{ match $kdl_node.get($property_name) { - Some(property) => { - match property.entries().iter().next() { - Some(first_entry) => { - match first_entry.value().as_string() { - Some(string_entry) => Some((string_entry, first_entry)), - None => { - return Err(ConfigError::new_kdl_error(format!("Property {} must be a string, found: {}", $property_name, first_entry.value()), property.span().offset(), property.span().len())); - } - } - }, + Some(property) => match property.entries().iter().next() { + Some(first_entry) => match first_entry.value().as_string() { + Some(string_entry) => Some((string_entry, first_entry)), None => { - return Err(ConfigError::new_kdl_error(format!("Property {} must have a value", $property_name), property.span().offset(), property.span().len())); - } - } + return Err(ConfigError::new_kdl_error( + format!( + "Property {} must be a string, found: {}", + $property_name, + first_entry.value() + ), + property.span().offset(), + property.span().len(), + )); + }, + }, + None => { + return Err(ConfigError::new_kdl_error( + format!("Property {} must have a value", $property_name), + property.span().offset(), + property.span().len(), + )); + }, }, - None => None + None => None, } - }} + }}; } #[macro_export] macro_rules! kdl_property_first_arg_as_bool_or_error { ( $kdl_node:expr, $property_name:expr ) => {{ match $kdl_node.get($property_name) { - Some(property) => { - match property.entries().iter().next() { - Some(first_entry) => { - match first_entry.value().as_bool() { - Some(bool_entry) => Some((bool_entry, first_entry)), - None => { - return Err(ConfigError::new_kdl_error(format!("Property {} must be true or false, found {}", $property_name, first_entry.value()), property.span().offset(), property.span().len())); - } - } - }, + Some(property) => match property.entries().iter().next() { + Some(first_entry) => match first_entry.value().as_bool() { + Some(bool_entry) => Some((bool_entry, first_entry)), None => { - return Err(ConfigError::new_kdl_error(format!("Property {} must have a value", $property_name), property.span().offset(), property.span().len())); - } - } + return Err(ConfigError::new_kdl_error( + format!( + "Property {} must be true or false, found {}", + $property_name, + first_entry.value() + ), + property.span().offset(), + property.span().len(), + )); + }, + }, + None => { + return Err(ConfigError::new_kdl_error( + format!("Property {} must have a value", $property_name), + property.span().offset(), + property.span().len(), + )); + }, }, - None => None + None => None, } - }} + }}; } #[macro_export] macro_rules! kdl_property_first_arg_as_i64_or_error { ( $kdl_node:expr, $property_name:expr ) => {{ match $kdl_node.get($property_name) { - Some(property) => { - match property.entries().iter().next() { - Some(first_entry) => { - match first_entry.value().as_i64() { - Some(int_entry) => Some((int_entry, first_entry)), - None => { - return Err(ConfigError::new_kdl_error(format!("Property {} must be numeric, found {}", $property_name, first_entry.value()), property.span().offset(), property.span().len())); - } - } - }, + Some(property) => match property.entries().iter().next() { + Some(first_entry) => match first_entry.value().as_i64() { + Some(int_entry) => Some((int_entry, first_entry)), None => { - return Err(ConfigError::new_kdl_error(format!("Property {} must have a value", $property_name), property.span().offset(), property.span().len())); - } - } + return Err(ConfigError::new_kdl_error( + format!( + "Property {} must be numeric, found {}", + $property_name, + first_entry.value() + ), + property.span().offset(), + property.span().len(), + )); + }, + }, + None => { + return Err(ConfigError::new_kdl_error( + format!("Property {} must have a value", $property_name), + property.span().offset(), + property.span().len(), + )); + }, }, - None => None + None => None, } - }} + }}; } #[macro_export] macro_rules! kdl_has_string_argument { ( $kdl_node:expr, $string_argument:expr ) => { - $kdl_node.entries().iter().find(|e| e.value().as_string() == Some($string_argument)).is_some() - } + $kdl_node + .entries() + .iter() + .find(|e| e.value().as_string() == Some($string_argument)) + .is_some() + }; } #[macro_export] macro_rules! kdl_children_property_first_arg_as_string { ( $kdl_node:expr, $property_name:expr ) => { - $kdl_node.children() + $kdl_node + .children() .and_then(|c| c.get($property_name)) .and_then(|p| p.entries().iter().next()) .and_then(|p| p.value().as_string()) - } + }; } #[macro_export] macro_rules! kdl_property_first_arg_as_bool { ( $kdl_node:expr, $property_name:expr ) => { - $kdl_node.get($property_name) + $kdl_node + .get($property_name) .and_then(|p| p.entries().iter().next()) .and_then(|p| p.value().as_bool()) - } + }; } #[macro_export] macro_rules! kdl_children_property_first_arg_as_bool { ( $kdl_node:expr, $property_name:expr ) => { - $kdl_node.children() + $kdl_node + .children() .and_then(|c| c.get($property_name)) .and_then(|p| p.entries().iter().next()) .and_then(|p| p.value().as_bool()) - } + }; } #[macro_export] macro_rules! kdl_property_first_arg_as_i64 { ( $kdl_node:expr, $property_name:expr ) => { - $kdl_node.get($property_name) + $kdl_node + .get($property_name) .and_then(|p| p.entries().iter().next()) .and_then(|p| p.value().as_i64()) - } + }; } #[macro_export] macro_rules! kdl_get_child { ( $kdl_node:expr, $child_name:expr ) => { - $kdl_node.children() - .and_then(|c| c.get($child_name)) - } + $kdl_node.children().and_then(|c| c.get($child_name)) + }; } #[macro_export] macro_rules! kdl_get_child_entry_bool_value { ( $kdl_node:expr, $child_name:expr ) => { - $kdl_node.children() + $kdl_node + .children() .and_then(|c| c.get($child_name)) .and_then(|c| c.get(0)) .and_then(|c| c.value().as_bool()) - } + }; } #[macro_export] macro_rules! kdl_get_child_entry_string_value { ( $kdl_node:expr, $child_name:expr ) => { - $kdl_node.children() + $kdl_node + .children() .and_then(|c| c.get($child_name)) .and_then(|c| c.get(0)) .and_then(|c| c.value().as_string()) - } + }; } #[macro_export] macro_rules! kdl_get_bool_property_or_child_value { ( $kdl_node:expr, $name:expr ) => { - $kdl_node.get($name) + $kdl_node + .get($name) .and_then(|e| e.value().as_bool()) - .or_else(|| $kdl_node.children() - .and_then(|c| c.get($name)) - .and_then(|c| c.get(0)) - .and_then(|c| c.value().as_bool()) - ) - } + .or_else(|| { + $kdl_node + .children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_bool()) + }) + }; } #[macro_export] macro_rules! kdl_get_bool_property_or_child_value_with_error { ( $kdl_node:expr, $name:expr ) => { match $kdl_node.get($name) { - Some(e) => { - match e.value().as_bool() { - Some(bool_value) => Some(bool_value), - None => { - return Err(kdl_parsing_error!( - format!("{} should be either true or false, found {}", $name, e.value()), - e - )) - } - } + Some(e) => match e.value().as_bool() { + Some(bool_value) => Some(bool_value), + None => { + return Err(kdl_parsing_error!( + format!( + "{} should be either true or false, found {}", + $name, + e.value() + ), + e + )) + }, }, None => { - let child_value = $kdl_node.children() + let child_value = $kdl_node + .children() .and_then(|c| c.get($name)) .and_then(|c| c.get(0)); match child_value { - Some(e) => { - match e.value().as_bool() { - Some(bool_value) => Some(bool_value), - None => { - return Err(kdl_parsing_error!( - format!("{} should be either true or false, found {}", $name, e.value()), - e - )) - } - } + Some(e) => match e.value().as_bool() { + Some(bool_value) => Some(bool_value), + None => { + return Err(kdl_parsing_error!( + format!( + "{} should be either true or false, found {}", + $name, + e.value() + ), + e + )) + }, }, None => { if let Some(child_node) = kdl_child_with_name!($kdl_node, $name) { return Err(kdl_parsing_error!( - format!("{} must have a value, eg. '{} true'", child_node.name().value(), child_node.name().value()), + format!( + "{} must have a value, eg. '{} true'", + child_node.name().value(), + child_node.name().value() + ), child_node - )) + )); } None - } + }, } - } + }, } - } + }; } #[macro_export] macro_rules! kdl_get_string_property_or_child_value { ( $kdl_node:expr, $name:expr ) => { - $kdl_node.get($name) + $kdl_node + .get($name) .and_then(|e| e.value().as_string()) - .or_else(|| $kdl_node.children() - .and_then(|c| c.get($name)) - .and_then(|c| c.get(0)) - .and_then(|c| c.value().as_string()) - ) - } + .or_else(|| { + $kdl_node + .children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_string()) + }) + }; } #[macro_export] macro_rules! kdl_property_or_child_value_node { ( $kdl_node:expr, $name:expr ) => { - $kdl_node.get($name) - .or_else(|| $kdl_node.children() + $kdl_node.get($name).or_else(|| { + $kdl_node + .children() .and_then(|c| c.get($name)) .and_then(|c| c.get(0)) - ) - } + }) + }; } #[macro_export] macro_rules! kdl_child_with_name { ( $kdl_node:expr, $name:expr ) => {{ - $kdl_node.children() - .and_then(|children| children.nodes() - .iter() - .find(|c| c.name().value() == $name) - ) - - }} + $kdl_node + .children() + .and_then(|children| children.nodes().iter().find(|c| c.name().value() == $name)) + }}; } #[macro_export] macro_rules! kdl_get_string_property_or_child_value_with_error { ( $kdl_node:expr, $name:expr ) => { match $kdl_node.get($name) { - Some(e) => { - match e.value().as_string() { - Some(string_value) => Some(string_value), - None => { - return Err(kdl_parsing_error!( - format!("{} should be a string, found {} - not a string", $name, e.value()), - e - )) - } - } + Some(e) => match e.value().as_string() { + Some(string_value) => Some(string_value), + None => { + return Err(kdl_parsing_error!( + format!( + "{} should be a string, found {} - not a string", + $name, + e.value() + ), + e + )) + }, }, None => { - let child_value = $kdl_node.children() + let child_value = $kdl_node + .children() .and_then(|c| c.get($name)) .and_then(|c| c.get(0)); match child_value { - Some(e) => { - match e.value().as_string() { - Some(string_value) => Some(string_value), - None => { - return Err(kdl_parsing_error!( - format!("{} should be a string, found {} - not a string", $name, e.value()), - e - )) - } - } + Some(e) => match e.value().as_string() { + Some(string_value) => Some(string_value), + None => { + return Err(kdl_parsing_error!( + format!( + "{} should be a string, found {} - not a string", + $name, + e.value() + ), + e + )) + }, }, None => { if let Some(child_node) = kdl_child_with_name!($kdl_node, $name) { return Err(kdl_parsing_error!( - format!("{} must have a value, eg. '{} \"foo\"'", child_node.name().value(), child_node.name().value()), + format!( + "{} must have a value, eg. '{} \"foo\"'", + child_node.name().value(), + child_node.name().value() + ), child_node - )) + )); } None - } + }, } - } + }, } - } + }; } #[macro_export] macro_rules! kdl_get_property_or_child { ( $kdl_node:expr, $name:expr ) => { - $kdl_node.get($name) + $kdl_node + .get($name) // .and_then(|e| e.value().as_string()) - .or_else(|| $kdl_node.children() - .and_then(|c| c.get($name)) - .and_then(|c| c.get(0)) - ) - } + .or_else(|| { + $kdl_node + .children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + }) + }; } #[macro_export] macro_rules! kdl_get_int_property_or_child_value { ( $kdl_node:expr, $name:expr ) => { - $kdl_node.get($name) + $kdl_node + .get($name) .and_then(|e| e.value().as_i64()) - .or_else(|| $kdl_node.children() - .and_then(|c| c.get($name)) - .and_then(|c| c.get(0)) - .and_then(|c| c.value().as_i64()) - ) - } + .or_else(|| { + $kdl_node + .children() + .and_then(|c| c.get($name)) + .and_then(|c| c.get(0)) + .and_then(|c| c.value().as_i64()) + }) + }; } #[macro_export] macro_rules! kdl_get_string_entry { ( $kdl_node:expr, $entry_name:expr ) => { - $kdl_node.get($entry_name) + $kdl_node + .get($entry_name) .and_then(|e| e.value().as_string()) - } + }; } #[macro_export] macro_rules! kdl_get_int_entry { ( $kdl_node:expr, $entry_name:expr ) => { - $kdl_node.get($entry_name) - .and_then(|e| e.value().as_i64()) - } + $kdl_node.get($entry_name).and_then(|e| e.value().as_i64()) + }; } - impl Options { pub fn from_kdl(kdl_options: &KdlDocument) -> Result { - let on_force_close = match kdl_property_first_arg_as_string_or_error!(kdl_options, "on_force_close") { - Some((string, entry)) => Some(OnForceClose::from_str(string).map_err(|_| { - kdl_parsing_error!(format!("Invalid value for on_force_close: '{}'", string), entry) - })?), - None => None - }; - let simplified_ui = kdl_property_first_arg_as_bool_or_error!(kdl_options, "simplified_ui").map(|(v, _)| v); - let default_shell = kdl_property_first_arg_as_string_or_error!(kdl_options, "default_shell") - .map(|(string, _entry)| PathBuf::from(string)); - let pane_frames = kdl_property_first_arg_as_bool_or_error!(kdl_options, "pane_frames").map(|(v, _)| v); + let on_force_close = + match kdl_property_first_arg_as_string_or_error!(kdl_options, "on_force_close") { + Some((string, entry)) => Some(OnForceClose::from_str(string).map_err(|_| { + kdl_parsing_error!( + format!("Invalid value for on_force_close: '{}'", string), + entry + ) + })?), + None => None, + }; + let simplified_ui = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "simplified_ui").map(|(v, _)| v); + let default_shell = + kdl_property_first_arg_as_string_or_error!(kdl_options, "default_shell") + .map(|(string, _entry)| PathBuf::from(string)); + let pane_frames = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "pane_frames").map(|(v, _)| v); let theme = kdl_property_first_arg_as_string_or_error!(kdl_options, "theme") .map(|(theme, _entry)| theme.to_string()); - let default_mode = match kdl_property_first_arg_as_string_or_error!(kdl_options, "default_mode") { - Some((string, entry)) => Some(InputMode::from_str(string).map_err(|_| { - kdl_parsing_error!(format!("Invalid input mode: '{}'", string), entry) - })?), - None => None - }; - let default_layout = kdl_property_first_arg_as_string_or_error!(kdl_options, "default_layout") - .map(|(string, _entry)| PathBuf::from(string)); + let default_mode = + match kdl_property_first_arg_as_string_or_error!(kdl_options, "default_mode") { + Some((string, entry)) => Some(InputMode::from_str(string).map_err(|_| { + kdl_parsing_error!(format!("Invalid input mode: '{}'", string), entry) + })?), + None => None, + }; + let default_layout = + kdl_property_first_arg_as_string_or_error!(kdl_options, "default_layout") + .map(|(string, _entry)| PathBuf::from(string)); let layout_dir = kdl_property_first_arg_as_string_or_error!(kdl_options, "layout_dir") .map(|(string, _entry)| PathBuf::from(string)); let theme_dir = kdl_property_first_arg_as_string_or_error!(kdl_options, "theme_dir") .map(|(string, _entry)| PathBuf::from(string)); - let mouse_mode = kdl_property_first_arg_as_bool_or_error!(kdl_options, "mouse_mode").map(|(v, _)| v); - let scroll_buffer_size = kdl_property_first_arg_as_i64_or_error!(kdl_options, "scroll_buffer_size") - .map(|(scroll_buffer_size, _entry)| scroll_buffer_size as usize); + let mouse_mode = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "mouse_mode").map(|(v, _)| v); + let scroll_buffer_size = + kdl_property_first_arg_as_i64_or_error!(kdl_options, "scroll_buffer_size") + .map(|(scroll_buffer_size, _entry)| scroll_buffer_size as usize); let copy_command = kdl_property_first_arg_as_string_or_error!(kdl_options, "copy_command") .map(|(copy_command, _entry)| copy_command.to_string()); - let copy_clipboard = match kdl_property_first_arg_as_string_or_error!(kdl_options, "copy_clipboard") { - Some((string, entry)) => Some(Clipboard::from_str(string).map_err(|_| { - kdl_parsing_error!(format!("Invalid value for copy_clipboard: '{}'", string), entry) - })?), - None => None - }; - let copy_on_select = kdl_property_first_arg_as_bool_or_error!(kdl_options, "copy_on_select").map(|(v, _)| v); - let scrollback_editor = kdl_property_first_arg_as_string_or_error!(kdl_options, "scrollback_editor") - .map(|(string, _entry)| PathBuf::from(string)); - let mirror_session = kdl_property_first_arg_as_bool_or_error!(kdl_options, "mirror_session").map(|(v, _)| v); + let copy_clipboard = + match kdl_property_first_arg_as_string_or_error!(kdl_options, "copy_clipboard") { + Some((string, entry)) => Some(Clipboard::from_str(string).map_err(|_| { + kdl_parsing_error!( + format!("Invalid value for copy_clipboard: '{}'", string), + entry + ) + })?), + None => None, + }; + let copy_on_select = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "copy_on_select").map(|(v, _)| v); + let scrollback_editor = + kdl_property_first_arg_as_string_or_error!(kdl_options, "scrollback_editor") + .map(|(string, _entry)| PathBuf::from(string)); + let mirror_session = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "mirror_session").map(|(v, _)| v); let session_name = kdl_property_first_arg_as_string_or_error!(kdl_options, "session_name") .map(|(session_name, _entry)| session_name.to_string()); - let attach_to_session = kdl_property_first_arg_as_bool_or_error!(kdl_options, "attach_to_session").map(|(v, _)| v); + let attach_to_session = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "attach_to_session") + .map(|(v, _)| v); Ok(Options { simplified_ui, theme, @@ -904,20 +1233,22 @@ impl Options { impl RunPlugin { pub fn from_kdl(kdl_node: &KdlNode) -> Result { - let _allow_exec_host_cmd = kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false); - let string_url = kdl_get_child_entry_string_value!(kdl_node, "location") - .ok_or( - ConfigError::new_kdl_error( - "Plugins must have a location".into(), - kdl_node.span().offset(), - kdl_node.span().len() - ) - )?; - let url = Url::parse(string_url).map_err(|e| ConfigError::new_kdl_error( - format!("Failed to parse url: {:?}", e), - kdl_node.span().offset(), - kdl_node.span().len(), - ))?; + let _allow_exec_host_cmd = + kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false); + let string_url = kdl_get_child_entry_string_value!(kdl_node, "location").ok_or( + ConfigError::new_kdl_error( + "Plugins must have a location".into(), + kdl_node.span().offset(), + kdl_node.span().len(), + ), + )?; + let url = Url::parse(string_url).map_err(|e| { + ConfigError::new_kdl_error( + format!("Failed to parse url: {:?}", e), + kdl_node.span().offset(), + kdl_node.span().len(), + ) + })?; let location = RunPluginLocation::try_from(url)?; Ok(RunPlugin { _allow_exec_host_cmd, @@ -927,7 +1258,7 @@ impl RunPlugin { } impl Layout { pub fn from_kdl(raw_layout: &str, file_name: String) -> Result { - KdlLayoutParser::new(raw_layout, file_name.clone()).parse().map_err(|e| { + KdlLayoutParser::new(raw_layout).parse().map_err(|e| { match e { ConfigError::KdlError(kdl_error) => ConfigError::KdlError(kdl_error.add_src(file_name, String::from(raw_layout))), ConfigError::KdlDeserializationError(kdl_error) => { @@ -955,11 +1286,18 @@ impl EnvironmentVariables { let mut env: HashMap = HashMap::new(); for env_var in kdl_children_nodes_or_error!(kdl_env_variables, "empty env variable block") { let env_var_name = kdl_name!(env_var); - let env_var_str_value = kdl_first_entry_as_string!(env_var).map(|s| format!("{}", s.to_string())); - let env_var_int_value = kdl_first_entry_as_i64!(env_var).map(|s| format!("{}", s.to_string())); - let env_var_value = env_var_str_value - .or(env_var_int_value) - .ok_or(ConfigError::new_kdl_error(format!("Failed to parse env var: {:?}", env_var_name), env_var.span().offset(), env_var.span().len()))?; + let env_var_str_value = + kdl_first_entry_as_string!(env_var).map(|s| format!("{}", s.to_string())); + let env_var_int_value = + kdl_first_entry_as_i64!(env_var).map(|s| format!("{}", s.to_string())); + let env_var_value = + env_var_str_value + .or(env_var_int_value) + .ok_or(ConfigError::new_kdl_error( + format!("Failed to parse env var: {:?}", env_var_name), + env_var.span().offset(), + env_var.span().len(), + ))?; env.insert(env_var_name.into(), env_var_value); } Ok(EnvironmentVariables::from_data(env)) @@ -967,7 +1305,10 @@ impl EnvironmentVariables { } impl Keybinds { - fn bind_keys_in_block(block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError> { + fn bind_keys_in_block( + block: &KdlNode, + input_mode_keybinds: &mut HashMap>, + ) -> Result<(), ConfigError> { let all_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode"); let bind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "bind"); let unbind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "unbind"); @@ -991,12 +1332,22 @@ impl Keybinds { } pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result { let clear_defaults = kdl_arg_is_truthy!(kdl_keybinds, "clear-defaults"); - let mut keybinds_from_config = if clear_defaults { Keybinds::default() } else { base_keybinds }; + let mut keybinds_from_config = if clear_defaults { + Keybinds::default() + } else { + base_keybinds + }; for block in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { if kdl_name!(block) == "shared_except" || kdl_name!(block) == "shared" { let mut modes_to_exclude = vec![]; for mode_name in kdl_string_arguments!(block) { - modes_to_exclude.push(InputMode::from_str(mode_name).map_err(|_| ConfigError::new_kdl_error(format!("Invalid mode: '{}'", mode_name), block.name().span().offset(), block.name().span().len()))?); + modes_to_exclude.push(InputMode::from_str(mode_name).map_err(|_| { + ConfigError::new_kdl_error( + format!("Invalid mode: '{}'", mode_name), + block.name().span().offset(), + block.name().span().len(), + ) + })?); } for mode in InputMode::iter() { if modes_to_exclude.contains(&mode) { @@ -1021,10 +1372,15 @@ impl Keybinds { } } for mode in kdl_children_nodes_or_error!(kdl_keybinds, "keybindings with no children") { - if kdl_name!(mode) == "unbind" || kdl_name!(mode) == "shared_except" || kdl_name!(mode) == "shared_among" || kdl_name!(mode) == "shared" { + if kdl_name!(mode) == "unbind" + || kdl_name!(mode) == "shared_except" + || kdl_name!(mode) == "shared_among" + || kdl_name!(mode) == "shared" + { continue; } - let mut input_mode_keybinds = Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?; + let mut input_mode_keybinds = + Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?; Keybinds::bind_keys_in_block(mode, &mut input_mode_keybinds)?; } if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) { @@ -1032,7 +1388,10 @@ impl Keybinds { }; Ok(keybinds_from_config) } - fn bind_actions_for_each_key(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ + fn bind_actions_for_each_key( + key_block: &KdlNode, + input_mode_keybinds: &mut HashMap>, + ) -> Result<(), ConfigError> { let keys: Vec = keys_from_kdl!(key_block); let actions: Vec = actions_from_kdl!(key_block); for key in keys { @@ -1040,14 +1399,20 @@ impl Keybinds { } Ok(()) } - fn unbind_keys(key_block: &KdlNode, input_mode_keybinds: &mut HashMap>) -> Result<(), ConfigError>{ + fn unbind_keys( + key_block: &KdlNode, + input_mode_keybinds: &mut HashMap>, + ) -> Result<(), ConfigError> { let keys: Vec = keys_from_kdl!(key_block); for key in keys { input_mode_keybinds.remove(&key); } Ok(()) } - fn unbind_keys_in_all_modes(global_unbind: &KdlNode, keybinds_from_config: &mut Keybinds) -> Result<(), ConfigError> { + fn unbind_keys_in_all_modes( + global_unbind: &KdlNode, + keybinds_from_config: &mut Keybinds, + ) -> Result<(), ConfigError> { let keys: Vec = keys_from_kdl!(global_unbind); for mode in keybinds_from_config.0.values_mut() { for key in &keys { @@ -1056,9 +1421,18 @@ impl Keybinds { } Ok(()) } - fn input_mode_keybindings <'a>(mode: &KdlNode, keybinds_from_config: &'a mut Keybinds) -> Result<&'a mut HashMap>, ConfigError> { + fn input_mode_keybindings<'a>( + mode: &KdlNode, + keybinds_from_config: &'a mut Keybinds, + ) -> Result<&'a mut HashMap>, ConfigError> { let mode_name = kdl_name!(mode); - let input_mode = InputMode::from_str(mode_name).map_err(|_| ConfigError::new_kdl_error(format!("Invalid mode: '{}'", mode_name), mode.name().span().offset(), mode.name().span().len()))?; + let input_mode = InputMode::from_str(mode_name).map_err(|_| { + ConfigError::new_kdl_error( + format!("Invalid mode: '{}'", mode_name), + mode.name().span().offset(), + mode.name().span().len(), + ) + })?; let input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&input_mode); let clear_defaults_for_mode = kdl_arg_is_truthy!(mode, "clear-defaults"); if clear_defaults_for_mode { @@ -1070,21 +1444,22 @@ impl Keybinds { impl RunCommand { pub fn from_kdl(kdl_node: &KdlNode) -> Result { - let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd") - .ok_or(ConfigError::new_kdl_error("Command must have a cmd value".into(), kdl_node.span().offset(), kdl_node.span().len()))? - ); + let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or( + ConfigError::new_kdl_error( + "Command must have a cmd value".into(), + kdl_node.span().offset(), + kdl_node.span().len(), + ), + )?); let cwd = kdl_get_child_entry_string_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); let args = match kdl_get_child!(kdl_node, "args") { - Some(kdl_args) => { - kdl_string_arguments!(kdl_args).iter().map(|s| String::from(*s)).collect() - }, - None => vec![] + Some(kdl_args) => kdl_string_arguments!(kdl_args) + .iter() + .map(|s| String::from(*s)) + .collect(), + None => vec![], }; - Ok(RunCommand { - command, - args, - cwd, - }) + Ok(RunCommand { command, args, cwd }) } } @@ -1122,14 +1497,21 @@ impl Config { impl PluginsConfig { pub fn from_kdl(kdl_plugin_config: &KdlNode) -> Result { let mut plugins: HashMap = HashMap::new(); - for plugin_config in kdl_children_nodes_or_error!(kdl_plugin_config, "no plugin config found") { + for plugin_config in + kdl_children_nodes_or_error!(kdl_plugin_config, "no plugin config found") + { let plugin_name = kdl_name!(plugin_config); let plugin_tag = PluginTag::new(plugin_name); let path = kdl_children_property_first_arg_as_string!(plugin_config, "path") .map(|path| PathBuf::from(path)) - .ok_or(ConfigError::new_kdl_error("Plugin path not found or invalid".into(), plugin_config.span().offset(), plugin_config.span().len()))?; - let allow_exec_host_cmd = kdl_children_property_first_arg_as_bool!(plugin_config, "_allow_exec_host_cmd") - .unwrap_or(false); + .ok_or(ConfigError::new_kdl_error( + "Plugin path not found or invalid".into(), + plugin_config.span().offset(), + plugin_config.span().len(), + ))?; + let allow_exec_host_cmd = + kdl_children_property_first_arg_as_bool!(plugin_config, "_allow_exec_host_cmd") + .unwrap_or(false); let plugin_config = PluginConfig { path, run: PluginType::Pane(None), @@ -1145,7 +1527,9 @@ impl UiConfig { pub fn from_kdl(kdl_ui_config: &KdlNode) -> Result { let mut ui_config = UiConfig::default(); if let Some(pane_frames) = kdl_get_child!(kdl_ui_config, "pane_frames") { - let rounded_corners = kdl_children_property_first_arg_as_bool!(pane_frames, "rounded_corners").unwrap_or(false); + let rounded_corners = + kdl_children_property_first_arg_as_bool!(pane_frames, "rounded_corners") + .unwrap_or(false); let frame_config = FrameConfig { rounded_corners }; ui_config.pane_frames = frame_config; } @@ -1173,7 +1557,7 @@ impl Themes { black: PaletteColor::try_from(("black", theme_colors))?, white: PaletteColor::try_from(("white", theme_colors))?, ..Default::default() - } + }, }; themes.insert(theme_name.into(), theme); } @@ -1189,24 +1573,31 @@ impl Theme { let mut kdl_config = String::new(); file.read_to_string(&mut kdl_config)?; let kdl_config: KdlDocument = kdl_config.parse()?; - let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::new_kdl_error("No theme found in file".into(), kdl_config.span().offset(), kdl_config.span().len()))?; + let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::new_kdl_error( + "No theme found in file".into(), + kdl_config.span().offset(), + kdl_config.span().len(), + ))?; let theme_name = kdl_name!(kdl_config); let theme_colors = kdl_children_or_error!(kdl_config, "empty theme"); - Ok((theme_name.into(), Theme { - palette: Palette { - fg: PaletteColor::try_from(("fg", theme_colors))?, - bg: PaletteColor::try_from(("bg", theme_colors))?, - red: PaletteColor::try_from(("red", theme_colors))?, - green: PaletteColor::try_from(("green", theme_colors))?, - yellow: PaletteColor::try_from(("yellow", theme_colors))?, - blue: PaletteColor::try_from(("blue", theme_colors))?, - magenta: PaletteColor::try_from(("magenta", theme_colors))?, - orange: PaletteColor::try_from(("orange", theme_colors))?, - cyan: PaletteColor::try_from(("cyan", theme_colors))?, - black: PaletteColor::try_from(("black", theme_colors))?, - white: PaletteColor::try_from(("white", theme_colors))?, - ..Default::default() - } - })) + Ok(( + theme_name.into(), + Theme { + palette: Palette { + fg: PaletteColor::try_from(("fg", theme_colors))?, + bg: PaletteColor::try_from(("bg", theme_colors))?, + red: PaletteColor::try_from(("red", theme_colors))?, + green: PaletteColor::try_from(("green", theme_colors))?, + yellow: PaletteColor::try_from(("yellow", theme_colors))?, + blue: PaletteColor::try_from(("blue", theme_colors))?, + magenta: PaletteColor::try_from(("magenta", theme_colors))?, + orange: PaletteColor::try_from(("orange", theme_colors))?, + cyan: PaletteColor::try_from(("cyan", theme_colors))?, + black: PaletteColor::try_from(("black", theme_colors))?, + white: PaletteColor::try_from(("white", theme_colors))?, + ..Default::default() + }, + }, + )) } } diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs index 47cf94be17..80cfb78396 100644 --- a/zellij-utils/src/lib.rs +++ b/zellij-utils/src/lib.rs @@ -3,11 +3,11 @@ pub mod consts; pub mod data; pub mod envs; pub mod input; +pub mod kdl; pub mod pane_size; pub mod position; pub mod setup; pub mod shared; -pub mod kdl; // The following modules can't be used when targeting wasm #[cfg(not(target_family = "wasm"))] @@ -21,6 +21,6 @@ pub mod logging; // Requires log4rs #[cfg(not(target_family = "wasm"))] pub use ::{ - anyhow, async_std, clap, interprocess, lazy_static, libc, nix, regex, serde, - signal_hook, tempfile, termwiz, vte, + anyhow, async_std, clap, interprocess, lazy_static, libc, nix, regex, serde, signal_hook, + tempfile, termwiz, vte, }; diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 3f6e0487e3..007cf752e8 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -83,8 +83,9 @@ impl Dimension { self.inner = inner; } - pub fn adjust_inner(&mut self, full_size: usize) -> f64 { // returns the leftover from - // rounding if any + pub fn adjust_inner(&mut self, full_size: usize) -> f64 { + // returns the leftover from + // rounding if any // TODO: elsewhere? match self.constraint { Constraint::Percent(percent) => { @@ -94,11 +95,11 @@ impl Dimension { self.set_inner(rounded as usize); leftover // self.set_inner(((percent / 100.0) * full_size as f64).round() as usize); - } + }, Constraint::Fixed(fixed_size) => { self.set_inner(fixed_size); 0.0 - } + }, } } pub fn increase_inner(&mut self, by: usize) { diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index ef22a37873..abd235eab3 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -1,3 +1,4 @@ +use crate::input::theme::Theme; use crate::{ cli::{CliArgs, Command}, consts::{ @@ -10,7 +11,6 @@ use crate::{ options::Options, }, }; -use crate::input::theme::Theme; use clap::{Args, IntoApp}; use clap_complete::Shell; use directories_next::BaseDirs; @@ -198,9 +198,7 @@ impl Setup { /// 2. layout options /// (`layout.yaml` / `zellij --layout`) /// 3. config options (`config.yaml`) - pub fn from_cli_args( - cli_args: &CliArgs, - ) -> Result<(Config, Layout, Options), ConfigError> { + pub fn from_cli_args(cli_args: &CliArgs) -> Result<(Config, Layout, Options), ConfigError> { let clean = cli_args.should_clean_config(); // note that this can potentially exit the process Setup::handle_setup_commands(cli_args); @@ -209,15 +207,17 @@ impl Setup { } else { Config::try_from(cli_args)? }; - let cli_config_options: Option = if let Some(Command::Options(options)) = cli_args.command.clone() { - Some(options.into()) - } else { - None - }; - let (layout, mut config) = Setup::parse_layout_and_override_config(cli_config_options.as_ref(), config, cli_args)?; + let cli_config_options: Option = + if let Some(Command::Options(options)) = cli_args.command.clone() { + Some(options.into()) + } else { + None + }; + let (layout, mut config) = + Setup::parse_layout_and_override_config(cli_config_options.as_ref(), config, cli_args)?; let config_options = match cli_config_options { Some(cli_config_options) => config.options.merge(cli_config_options), - None => config.options.clone() + None => config.options.clone(), }; if let Some(theme_dir) = config_options @@ -238,7 +238,6 @@ impl Setup { } } - if let Some(Command::Setup(ref setup)) = &cli_args.command { setup .from_cli_with_options(cli_args, &config_options) @@ -442,19 +441,30 @@ impl Setup { _ => {}, } } - fn parse_layout_and_override_config(cli_config_options: Option<&Options>, config: Config, cli_args: &CliArgs) -> Result<(Layout, Config), ConfigError> { + fn parse_layout_and_override_config( + cli_config_options: Option<&Options>, + config: Config, + cli_args: &CliArgs, + ) -> Result<(Layout, Config), ConfigError> { // find the layout folder relative to which we'll look for our layout - let layout_dir = cli_config_options.as_ref() + let layout_dir = cli_config_options + .as_ref() .and_then(|cli_options| cli_options.layout_dir.clone()) .or_else(|| config.options.layout_dir.clone()) - .or_else(|| get_layout_dir(cli_args.config_dir.clone().or_else(find_default_config_dir))); + .or_else(|| { + get_layout_dir(cli_args.config_dir.clone().or_else(find_default_config_dir)) + }); // the chosen layout can either be a path relative to the layout_dir or a name of one // of our assets, this distinction is made when parsing the layout - TODO: ideally, this // logic should not be split up and all the decisions should happen here let chosen_layout = cli_args .layout .clone() - .or_else(|| cli_config_options.as_ref().and_then(|cli_options| cli_options.default_layout.clone())) + .or_else(|| { + cli_config_options + .as_ref() + .and_then(|cli_options| cli_options.default_layout.clone()) + }) .or_else(|| config.options.default_layout.clone()); // we merge-override the config here because the layout might contain configuration // that needs to take precedence @@ -471,18 +481,15 @@ impl Setup { ); }; } - } #[cfg(test)] mod setup_test { use super::Setup; - use std::path::PathBuf; - use insta::assert_snapshot; use crate::cli::{CliArgs, Command}; - use crate::input::{ - options::{Options, CliOptions}, - }; + use crate::input::options::{CliOptions, Options}; + use insta::assert_snapshot; + use std::path::PathBuf; #[test] fn default_config_with_no_cli_arguments() { @@ -495,22 +502,23 @@ mod setup_test { #[test] fn cli_arguments_override_config_options() { let mut cli_args = CliArgs::default(); - cli_args.command = Some(Command::Options( - CliOptions { - options: Options { - simplified_ui: Some(true), - ..Default::default() - }, + cli_args.command = Some(Command::Options(CliOptions { + options: Options { + simplified_ui: Some(true), ..Default::default() - } - )); + }, + ..Default::default() + })); let (_config, _layout, options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", options)); } #[test] fn layout_options_override_config_options() { let mut cli_args = CliArgs::default(); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-options.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-options.kdl", + env!("CARGO_MANIFEST_DIR") + ))); let (_config, layout, options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", options)); assert_snapshot!(format!("{:#?}", layout)); @@ -518,16 +526,17 @@ mod setup_test { #[test] fn cli_arguments_override_layout_options() { let mut cli_args = CliArgs::default(); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-options.kdl", env!("CARGO_MANIFEST_DIR")))); - cli_args.command = Some(Command::Options( - CliOptions { - options: Options { - pane_frames: Some(true), - ..Default::default() - }, + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-options.kdl", + env!("CARGO_MANIFEST_DIR") + ))); + cli_args.command = Some(Command::Options(CliOptions { + options: Options { + pane_frames: Some(true), ..Default::default() - } - )); + }, + ..Default::default() + })); let (_config, layout, options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", options)); assert_snapshot!(format!("{:#?}", layout)); @@ -535,40 +544,70 @@ mod setup_test { #[test] fn layout_env_vars_override_config_env_vars() { let mut cli_args = CliArgs::default(); - cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-env-vars.kdl", env!("CARGO_MANIFEST_DIR")))); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-env-vars.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.config = Some(PathBuf::from(format!( + "{}/src/test-fixtures/config-with-env-vars.kdl", + env!("CARGO_MANIFEST_DIR") + ))); + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-env-vars.kdl", + env!("CARGO_MANIFEST_DIR") + ))); let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", config)); } #[test] fn layout_ui_config_overrides_config_ui_config() { let mut cli_args = CliArgs::default(); - cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-ui-config.kdl", env!("CARGO_MANIFEST_DIR")))); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-ui-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.config = Some(PathBuf::from(format!( + "{}/src/test-fixtures/config-with-ui-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-ui-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", config)); } #[test] fn layout_plugins_override_config_plugins() { let mut cli_args = CliArgs::default(); - cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-plugins-config.kdl", env!("CARGO_MANIFEST_DIR")))); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-plugins-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.config = Some(PathBuf::from(format!( + "{}/src/test-fixtures/config-with-plugins-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-plugins-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", config)); } #[test] fn layout_themes_override_config_themes() { let mut cli_args = CliArgs::default(); - cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-themes-config.kdl", env!("CARGO_MANIFEST_DIR")))); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-themes-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.config = Some(PathBuf::from(format!( + "{}/src/test-fixtures/config-with-themes-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-themes-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", config)); } #[test] fn layout_keybinds_override_config_keybinds() { let mut cli_args = CliArgs::default(); - cli_args.config = Some(PathBuf::from(format!("{}/src/test-fixtures/config-with-keybindings-config.kdl", env!("CARGO_MANIFEST_DIR")))); - cli_args.layout = Some(PathBuf::from(format!("{}/src/test-fixtures/layout-with-keybindings-config.kdl", env!("CARGO_MANIFEST_DIR")))); + cli_args.config = Some(PathBuf::from(format!( + "{}/src/test-fixtures/config-with-keybindings-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); + cli_args.layout = Some(PathBuf::from(format!( + "{}/src/test-fixtures/layout-with-keybindings-config.kdl", + env!("CARGO_MANIFEST_DIR") + ))); let (config, _layout, _options) = Setup::from_cli_args(&cli_args).unwrap(); assert_snapshot!(format!("{:#?}", config)); } From 8f42392b5f005190dd710ffadd7f4423536e34c1 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 30 Sep 2022 14:35:53 +0200 Subject: [PATCH 41/55] fix: e2e tests --- src/tests/e2e/remote_runner.rs | 56 ---------------------------------- 1 file changed, 56 deletions(-) diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 03fb619e11..b0bde1dbc3 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -19,7 +19,6 @@ use std::rc::Rc; const ZELLIJ_EXECUTABLE_LOCATION: &str = "/usr/src/zellij/x86_64-unknown-linux-musl/release/zellij"; const SET_ENV_VARIABLES: &str = "EDITOR=/usr/bin/vi"; -const ZELLIJ_LAYOUT_PATH: &str = "/usr/src/zellij/fixtures/layouts"; const ZELLIJ_CONFIG_PATH: &str = "/usr/src/zellij/fixtures/configs"; const ZELLIJ_DATA_DIR: &str = "/usr/src/zellij/e2e-data"; const ZELLIJ_FIXTURE_PATH: &str = "/usr/src/zellij/fixtures"; @@ -145,25 +144,6 @@ fn start_zellij_without_frames(channel: &mut ssh2::Channel) { std::thread::sleep(std::time::Duration::from_secs(1)); // wait until Zellij stops parsing startup ANSI codes from the terminal STDIN } -fn start_zellij_with_layout(channel: &mut ssh2::Channel, layout_path: &str) { - stop_zellij(channel); - channel - .write_all( - format!( - "{} {} --layout {} --session {} --data-dir {}\n", - SET_ENV_VARIABLES, - ZELLIJ_EXECUTABLE_LOCATION, - layout_path, - SESSION_NAME, - ZELLIJ_DATA_DIR - ) - .as_bytes(), - ) - .unwrap(); - channel.flush().unwrap(); - std::thread::sleep(std::time::Duration::from_secs(1)); // wait until Zellij stops parsing startup ANSI codes from the terminal STDIN -} - fn start_zellij_with_config(channel: &mut ssh2::Channel, config_path: &str) { stop_zellij(channel); channel @@ -571,42 +551,6 @@ impl RemoteRunner { reader_thread, } } - pub fn new_with_layout(win_size: Size, layout_file_name: &'static str) -> Self { - let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name); - let sess = ssh_connect(); - let mut channel = sess.channel_session().unwrap(); - let mut rows = Dimension::fixed(win_size.rows); - let mut cols = Dimension::fixed(win_size.cols); - rows.set_inner(win_size.rows); - cols.set_inner(win_size.cols); - let pane_geom = PaneGeom { - x: 0, - y: 0, - rows, - cols, - }; - setup_remote_environment(&mut channel, win_size); - start_zellij_with_layout(&mut channel, &remote_path.to_string_lossy()); - let channel = Arc::new(Mutex::new(channel)); - let last_snapshot = Arc::new(Mutex::new(String::new())); - let cursor_coordinates = Arc::new(Mutex::new((0, 0))); - sess.set_blocking(false); - let reader_thread = - read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); - RemoteRunner { - steps: vec![], - channel, - currently_running_step: None, - current_step_index: 0, - retries_left: RETRIES, - retry_pause_ms: 100, - test_timed_out: false, - panic_on_no_retries_left: true, - last_snapshot, - cursor_coordinates, - reader_thread, - } - } pub fn new_with_config(win_size: Size, config_file_name: &'static str) -> Self { let remote_path = Path::new(ZELLIJ_CONFIG_PATH).join(config_file_name); let sess = ssh_connect(); From d9624d888da874c3be155a85dfe3ab079f90621e Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sun, 2 Oct 2022 13:40:24 +0200 Subject: [PATCH 42/55] fix(screen): propagate errors after merge --- zellij-server/src/screen.rs | 32 +++++++++---------- .../src/tab/unit/tab_integration_tests.rs | 2 +- zellij-server/src/unit/screen_tests.rs | 3 +- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 7ae60f4a85..229dd6f011 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -603,9 +603,7 @@ impl Screen { self.close_tab_at_index(active_tab_index) .with_context(err_context) }, - None => { - Ok(()) - } + None => Ok(()), } } @@ -744,7 +742,12 @@ impl Screen { /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. - pub fn new_tab(&mut self, layout: PaneLayout, new_pids: Vec, client_id: ClientId) -> Result<()> { + pub fn new_tab( + &mut self, + layout: PaneLayout, + new_pids: Vec, + client_id: ClientId, + ) -> Result<()> { let client_id = if self.get_active_tab(client_id).is_some() { client_id } else if let Some(first_client_id) = self.get_first_client_id() { @@ -937,9 +940,7 @@ impl Screen { Ok(()) } }, - None => { - Ok(()) - } + None => Ok(()), } } pub fn undo_active_rename_tab(&mut self, client_id: ClientId) -> Result<()> { @@ -961,10 +962,7 @@ impl Screen { } Ok(()) }, - None => { - Ok(()) - - } + None => Ok(()), } } @@ -1186,8 +1184,8 @@ pub(crate) fn screen_thread_main( .toggle_floating_panes(client_id, default_shell) ); screen.unblock_input(); - screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins - screen.render(); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::ShowFloatingPanes(client_id) => { active_tab_and_connected_client_id!( @@ -1196,8 +1194,8 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, _client_id: ClientId| tab.show_floating_panes() ); screen.unblock_input(); - screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins - screen.render(); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::HideFloatingPanes(client_id) => { active_tab_and_connected_client_id!( @@ -1553,7 +1551,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.undo_active_rename_pane(client_id) ); - screen.render(); + screen.render()?; screen.render()?; }, ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => { @@ -1637,7 +1635,7 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::ChangeModeForAllClients(mode_info) => { screen.change_mode_for_all_clients(mode_info); - screen.render(); + screen.render()?; }, ScreenInstruction::ToggleActiveSyncTab(client_id) => { active_tab_and_connected_client_id!( diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 84f2e1c240..3b26bc26eb 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -12,8 +12,8 @@ use crate::{ use std::path::PathBuf; use zellij_utils::channels::Receiver; use zellij_utils::envs::set_session_name; -use zellij_utils::input::layout::{Layout, PaneLayout}; use zellij_utils::errors::ErrorContext; +use zellij_utils::input::layout::{Layout, PaneLayout}; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::position::Position; diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 3190a18eb9..c86fdb94b2 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -411,7 +411,8 @@ macro_rules! log_actions_in_thread { fn new_tab(screen: &mut Screen, pid: i32) { let client_id = 1; - screen.new_tab(PaneLayout::default(), vec![pid], client_id) + screen + .new_tab(PaneLayout::default(), vec![pid], client_id) .expect("TEST"); } From 05a6a9995cd2ed24e0dd1c76c02b379ad715b3a2 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sun, 2 Oct 2022 13:45:19 +0200 Subject: [PATCH 43/55] style(clippy): lower clippy level --- Makefile.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.toml b/Makefile.toml index f8ade7e644..0906d02f27 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -83,7 +83,7 @@ args = [ # Simple clippy tweak [tasks.clippy] -args = ["clippy", "--all-targets","--all-features","--","--deny","warnings", "@@split(CARGO_MAKE_TASK_ARGS,;)"] +args = ["clippy", "--all-targets", "--all-features", "@@split(CARGO_MAKE_TASK_ARGS,;)"] # Release building and installing Zellij [tasks.install] From c3fd53c25b2e84269c6fe4ef10e5f5538533e820 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sun, 2 Oct 2022 14:58:24 +0200 Subject: [PATCH 44/55] fix(tests): own session_name variable --- zellij-server/src/unit/screen_tests.rs | 5 ++++- ..._cli_go_to_next_and_previous_search_occurrence.snap | 4 ++-- ...ver__screen__screen_tests__send_cli_rename_tab.snap | 10 +++++----- ...screen__screen_tests__send_cli_undo_rename_tab.snap | 10 +++++----- ...een__screen_tests__send_cli_update_search_term.snap | 4 ++-- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index c86fdb94b2..5f615fbdf0 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -21,6 +21,7 @@ use zellij_utils::pane_size::{Size, SizeInPixels}; use crate::pty_writer::PtyWriteInstruction; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; +use std::env::set_var; use crate::{pty::PtyInstruction, wasm_vm::PluginInstruction}; use zellij_utils::ipc::PixelDimensions; @@ -205,7 +206,8 @@ fn create_new_screen(size: Size) -> Screen { ..Default::default() }; let max_panes = None; - let mode_info = ModeInfo::default(); + let mut mode_info = ModeInfo::default(); + mode_info.session_name = Some("zellij-test".into()); let draw_pane_frames = false; let session_is_mirrored = true; let copy_options = CopyOptions::default(); @@ -256,6 +258,7 @@ impl MockScreen { let screen_thread = std::thread::Builder::new() .name("screen_thread".to_string()) .spawn(move || { + set_var("ZELLIJ_SESSION_NAME", "zellij-test"); screen_thread_main( screen_bus, None, diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap index 7d750dbb51..0213fd278a 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2269 +assertion_line: 2674 expression: "format!(\"{:?}\", * received_server_instructions.lock().unwrap())" --- -[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] +[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap index 94dc4eb2d7..40b651ae95 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2154 +assertion_line: 2528 expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" --- [ @@ -89,7 +89,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), @@ -165,7 +165,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), @@ -261,7 +261,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), @@ -337,7 +337,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap index d7950b1d55..dbe2c85ab9 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2190 +assertion_line: 2571 expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" --- [ @@ -89,7 +89,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), @@ -165,7 +165,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), @@ -261,7 +261,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), @@ -337,7 +337,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" arrow_fonts: false, }, session_name: Some( - "zellij", + "zellij-test", ), }, ), diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap index 369080d8d3..9421b96e2e 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2221 +assertion_line: 2614 expression: "format!(\"{:?}\", * received_server_instructions.lock().unwrap())" --- -[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] +[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] From 0a9937f7ed6f578578cdb1e8cbb8c41818b20271 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sun, 2 Oct 2022 14:58:47 +0200 Subject: [PATCH 45/55] style(fmt): rustfmt --- zellij-server/src/unit/screen_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 5f615fbdf0..ee0cc9e967 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -19,9 +19,9 @@ use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; use crate::pty_writer::PtyWriteInstruction; +use std::env::set_var; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; -use std::env::set_var; use crate::{pty::PtyInstruction, wasm_vm::PluginInstruction}; use zellij_utils::ipc::PixelDimensions; From f1c400fddd64a22daa83b0a5abc2950dbc6f493f Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 11:32:24 +0200 Subject: [PATCH 46/55] fix(cli): various action fixes --- src/main.rs | 2 - zellij-server/src/panes/terminal_pane.rs | 1 + zellij-server/src/route.rs | 3 +- zellij-server/src/screen.rs | 51 ++++- zellij-server/src/unit/screen_tests.rs | 197 +----------------- ...o_next_and_previous_search_occurrence.snap | 2 +- ..._new_pane_action_with_command_and_cwd.snap | 6 + ...s__send_cli_undo_rename_pane_action-5.snap | 16 +- zellij-utils/src/cli.rs | 12 -- zellij-utils/src/input/actions.rs | 20 +- 10 files changed, 69 insertions(+), 241 deletions(-) create mode 100644 zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap diff --git a/src/main.rs b/src/main.rs index 793fdcf511..d2363e7bb4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,6 @@ fn main() { command, direction, cwd, - args, floating, })) = opts.command { @@ -31,7 +30,6 @@ fn main() { command, direction, cwd, - args, floating, }; commands::send_action_to_session(command_cli_action, opts.session); diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index d8b6c6054e..8b1beeac96 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -427,6 +427,7 @@ impl Pane for TerminalPane { self.pane_name.push_str(c); }, } + self.set_should_render(true); } fn pid(&self) -> PaneId { PaneId::Terminal(self.pid) diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 11e7e2e616..75230488de 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -142,7 +142,8 @@ pub(crate) fn route_action( let screen_instr = match direction { Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id), Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab(client_id), - _ => unreachable!(), + Direction::Up => ScreenInstruction::SwitchTabNext(client_id), + Direction::Down => ScreenInstruction::SwitchTabPrev(client_id), }; session.senders.send_to_screen(screen_instr).unwrap(); }, diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 229dd6f011..1eee86c165 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -547,7 +547,7 @@ impl Screen { } pub fn go_to_tab(&mut self, tab_index: usize, client_id: ClientId) -> Result<()> { - self.switch_active_tab(tab_index - 1, client_id) + self.switch_active_tab(tab_index.saturating_sub(1), client_id) } fn close_tab_at_index(&mut self, tab_index: usize) -> Result<()> { @@ -1245,6 +1245,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_left(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::ResizeRight(client_id) => { @@ -1253,6 +1254,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_right(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::ResizeDown(client_id) => { @@ -1261,6 +1263,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_down(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::ResizeUp(client_id) => { @@ -1269,6 +1272,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_up(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::ResizeIncrease(client_id) => { @@ -1277,6 +1281,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_increase(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::ResizeDecrease(client_id) => { @@ -1285,6 +1290,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.resize_decrease(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::SwitchFocus(client_id) => { @@ -1293,6 +1299,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.focus_next_pane(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::FocusNextPane(client_id) => { @@ -1302,6 +1309,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.focus_next_pane(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::FocusPreviousPane(client_id) => { active_tab_and_connected_client_id!( @@ -1310,6 +1318,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.focus_previous_pane(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MoveFocusLeft(client_id) => { active_tab_and_connected_client_id!( @@ -1318,6 +1327,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_left(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id) => { screen.move_focus_left_or_previous_tab(client_id)?; @@ -1331,6 +1341,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_down(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MoveFocusRight(client_id) => { active_tab_and_connected_client_id!( @@ -1339,6 +1350,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_right(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MoveFocusRightOrNextTab(client_id) => { screen.move_focus_right_or_next_tab(client_id)?; @@ -1352,6 +1364,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_up(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::DumpScreen(file, client_id) => { active_tab_and_connected_client_id!( @@ -1361,6 +1374,7 @@ pub(crate) fn screen_thread_main( .dump_active_terminal_screen(Some(file.to_string()), client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::EditScrollback(client_id) => { active_tab_and_connected_client_id!( @@ -1376,6 +1390,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.scroll_active_terminal_up(client_id) ); + screen.unblock_input(); screen.render()?; }, ScreenInstruction::MovePane(client_id) => { @@ -1385,6 +1400,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_active_pane(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MovePaneDown(client_id) => { active_tab_and_connected_client_id!( @@ -1393,6 +1409,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_down(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MovePaneUp(client_id) => { active_tab_and_connected_client_id!( @@ -1401,6 +1418,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_up(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MovePaneRight(client_id) => { active_tab_and_connected_client_id!( @@ -1409,6 +1427,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_right(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MovePaneLeft(client_id) => { active_tab_and_connected_client_id!( @@ -1417,6 +1436,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_left(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ScrollUpAt(point, client_id) => { active_tab_and_connected_client_id!( @@ -1426,6 +1446,7 @@ pub(crate) fn screen_thread_main( .handle_scrollwheel_up(&point, 3, client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ScrollDown(client_id) => { active_tab_and_connected_client_id!( @@ -1434,6 +1455,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.scroll_active_terminal_down(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ScrollDownAt(point, client_id) => { active_tab_and_connected_client_id!( @@ -1443,6 +1465,7 @@ pub(crate) fn screen_thread_main( .handle_scrollwheel_down(&point, 3, client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ScrollToBottom(client_id) => { active_tab_and_connected_client_id!( @@ -1452,6 +1475,7 @@ pub(crate) fn screen_thread_main( .scroll_active_terminal_to_bottom(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::PageScrollUp(client_id) => { active_tab_and_connected_client_id!( @@ -1461,6 +1485,7 @@ pub(crate) fn screen_thread_main( .scroll_active_terminal_up_page(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::PageScrollDown(client_id) => { active_tab_and_connected_client_id!( @@ -1470,6 +1495,7 @@ pub(crate) fn screen_thread_main( .scroll_active_terminal_down_page(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::HalfPageScrollUp(client_id) => { active_tab_and_connected_client_id!( @@ -1479,6 +1505,7 @@ pub(crate) fn screen_thread_main( .scroll_active_terminal_up_half_page(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::HalfPageScrollDown(client_id) => { active_tab_and_connected_client_id!( @@ -1488,6 +1515,7 @@ pub(crate) fn screen_thread_main( .scroll_active_terminal_down_half_page(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ClearScroll(client_id) => { active_tab_and_connected_client_id!( @@ -1497,6 +1525,7 @@ pub(crate) fn screen_thread_main( .clear_active_terminal_scroll(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::CloseFocusedPane(client_id) => { active_tab_and_connected_client_id!( @@ -1506,6 +1535,7 @@ pub(crate) fn screen_thread_main( ); screen.update_tabs()?; screen.render()?; + screen.unblock_input(); }, ScreenInstruction::SetSelectable(id, selectable, tab_index) => { screen.get_indexed_tab_mut(tab_index).map_or_else( @@ -1536,6 +1566,7 @@ pub(crate) fn screen_thread_main( }, } screen.update_tabs()?; + screen.unblock_input(); }, ScreenInstruction::UpdatePaneName(c, client_id) => { active_tab_and_connected_client_id!( @@ -1544,6 +1575,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.update_active_pane_name(c, client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::UndoRenamePane(client_id) => { active_tab_and_connected_client_id!( @@ -1552,7 +1584,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.undo_active_rename_pane(client_id) ); screen.render()?; - screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => { active_tab_and_connected_client_id!( @@ -1563,6 +1595,7 @@ pub(crate) fn screen_thread_main( ); screen.update_tabs()?; screen.render()?; + screen.unblock_input(); }, ScreenInstruction::TogglePaneFrames => { screen.draw_pane_frames = !screen.draw_pane_frames; @@ -1570,6 +1603,7 @@ pub(crate) fn screen_thread_main( tab.set_pane_frames(screen.draw_pane_frames); } screen.render()?; + screen.unblock_input(); }, ScreenInstruction::SwitchTabNext(client_id) => { screen.switch_tab_next(client_id)?; @@ -1607,10 +1641,12 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::UpdateTabName(c, client_id) => { screen.update_active_tab_name(c, client_id)?; + screen.unblock_input(); screen.render()?; }, ScreenInstruction::UndoRenameTab(client_id) => { screen.undo_active_rename_tab(client_id)?; + screen.unblock_input(); screen.render()?; }, ScreenInstruction::TerminalResize(new_size) => { @@ -1632,10 +1668,12 @@ pub(crate) fn screen_thread_main( ScreenInstruction::ChangeMode(mode_info, client_id) => { screen.change_mode(mode_info, client_id); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ChangeModeForAllClients(mode_info) => { screen.change_mode_for_all_clients(mode_info); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::ToggleActiveSyncTab(client_id) => { active_tab_and_connected_client_id!( @@ -1645,29 +1683,34 @@ pub(crate) fn screen_thread_main( ); screen.update_tabs()?; screen.render()?; + screen.unblock_input(); }, ScreenInstruction::LeftClick(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .handle_left_click(&point, client_id)); screen.update_tabs()?; screen.render()?; + screen.unblock_input(); }, ScreenInstruction::RightClick(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .handle_right_click(&point, client_id)); screen.update_tabs()?; screen.render()?; + screen.unblock_input(); }, ScreenInstruction::MiddleClick(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .handle_middle_click(&point, client_id)); screen.update_tabs()?; screen.render()?; + screen.unblock_input(); }, ScreenInstruction::LeftMouseRelease(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .handle_left_mouse_release(&point, client_id)); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::RightMouseRelease(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab @@ -1765,6 +1808,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.search_up(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::SearchToggleCaseSensitivity(client_id) => { active_tab_and_connected_client_id!( @@ -1774,6 +1818,7 @@ pub(crate) fn screen_thread_main( .toggle_search_case_sensitivity(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::SearchToggleWrap(client_id) => { active_tab_and_connected_client_id!( @@ -1782,6 +1827,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.toggle_search_wrap(client_id) ); screen.render()?; + screen.unblock_input(); }, ScreenInstruction::SearchToggleWholeWord(client_id) => { active_tab_and_connected_client_id!( @@ -1790,6 +1836,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.toggle_search_whole_words(client_id) ); screen.render()?; + screen.unblock_input(); }, } } diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index ee0cc9e967..16b962f1ac 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1802,7 +1802,6 @@ pub fn send_cli_new_pane_action_with_default_parameters() { direction: None, command: None, cwd: None, - args: None, floating: None, }; send_cli_action_to_server( @@ -1840,7 +1839,6 @@ pub fn send_cli_new_pane_action_with_split_direction() { direction: Some(Direction::Right), command: None, cwd: None, - args: None, floating: None, }; send_cli_action_to_server( @@ -1855,7 +1853,7 @@ pub fn send_cli_new_pane_action_with_split_direction() { } #[test] -pub fn send_cli_new_pane_action_with_command_cwd_and_args() { +pub fn send_cli_new_pane_action_with_command_and_cwd() { let size = Size { cols: 121, rows: 20, @@ -1878,7 +1876,6 @@ pub fn send_cli_new_pane_action_with_command_cwd_and_args() { direction: Some(Direction::Right), command: Some("htop".into()), cwd: Some("/some/folder".into()), - args: Some("-h --something arg".into()), floating: None, }; send_cli_action_to_server( @@ -2175,95 +2172,6 @@ pub fn send_cli_close_pane_action() { assert_snapshot!(format!("{}", snapshot_count)); } -#[test] -pub fn send_cli_rename_pane_action() { - let size = Size { cols: 80, rows: 10 }; - let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); - initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut mock_screen = MockScreen::new(size); - let session_metadata = mock_screen.clone_session_metadata(); - let screen_thread = mock_screen.run(Some(initial_layout)); - let received_server_instructions = Arc::new(Mutex::new(vec![])); - let server_receiver = mock_screen.server_receiver.take().unwrap(); - let server_instruction = log_actions_in_thread!( - received_server_instructions, - ServerInstruction::KillSession, - server_receiver - ); - let rename_pane_action = CliAction::RenamePane { - name: "my_new_pane_title".into(), - }; - send_cli_action_to_server( - &session_metadata, - rename_pane_action, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( - received_server_instructions.lock().unwrap().iter(), - size, - ); - let snapshot_count = snapshots.len(); - for (_cursor_coordinates, snapshot) in snapshots { - assert_snapshot!(format!("{}", snapshot)); - } - assert_snapshot!(format!("{}", snapshot_count)); -} - -#[test] -pub fn send_cli_undo_rename_pane_action() { - let size = Size { cols: 80, rows: 10 }; - let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); - initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut mock_screen = MockScreen::new(size); - let session_metadata = mock_screen.clone_session_metadata(); - let screen_thread = mock_screen.run(Some(initial_layout)); - let received_server_instructions = Arc::new(Mutex::new(vec![])); - let server_receiver = mock_screen.server_receiver.take().unwrap(); - let server_instruction = log_actions_in_thread!( - received_server_instructions, - ServerInstruction::KillSession, - server_receiver - ); - let rename_pane_action = CliAction::RenamePane { - name: "my_new_pane_title".into(), - }; - let undo_rename_pane_action = CliAction::UndoRenamePane; - // first rename the pane - send_cli_action_to_server( - &session_metadata, - rename_pane_action, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - // then undo the rename, returning to the default name - send_cli_action_to_server( - &session_metadata, - undo_rename_pane_action, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - mock_screen.teardown(vec![server_instruction, screen_thread]); - let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( - received_server_instructions.lock().unwrap().iter(), - size, - ); - let snapshot_count = snapshots.len(); - for (_cursor_coordinates, snapshot) in snapshots { - println!("--- snapshot: {}", snapshot); - assert_snapshot!(format!("{}", snapshot)); - } - assert_snapshot!(format!("{}", snapshot_count)); -} - #[test] pub fn send_cli_new_tab_action_default_params() { let size = Size { cols: 80, rows: 10 }; @@ -2573,106 +2481,3 @@ pub fn send_cli_undo_rename_tab() { *received_plugin_instructions.lock().unwrap() )) } - -#[test] -pub fn send_cli_update_search_term() { - // the output (snapshot) in this test is unfortuantely not very Human readable - // this is because the search term only updates the styling, and we don't have a good way to - // snapshot styling (we usually strip them) - // - // if this test fails, it means something it wrong with searching through the CLI - let size = Size { cols: 80, rows: 10 }; - let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); - initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); - second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut mock_screen = MockScreen::new(size); - mock_screen.new_tab(second_tab_layout); - let session_metadata = mock_screen.clone_session_metadata(); - let screen_thread = mock_screen.run(Some(initial_layout)); - let received_server_instructions = Arc::new(Mutex::new(vec![])); - let server_receiver = mock_screen.server_receiver.take().unwrap(); - let server_thread = log_actions_in_thread!( - received_server_instructions, - ServerInstruction::KillSession, - server_receiver - ); - let update_search_term = CliAction::SearchInput { - input: "my-search-term".into(), - }; - send_cli_action_to_server( - &session_metadata, - update_search_term, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - mock_screen.teardown(vec![server_thread, screen_thread]); - assert_snapshot!(format!( - "{:?}", - *received_server_instructions.lock().unwrap() - )); -} - -#[test] -pub fn send_cli_go_to_next_and_previous_search_occurrence() { - // the output (snapshot) in this test is unfortuantely not very Human readable - // this is because the search term only updates the styling, and we don't have a good way to - // snapshot styling (we usually strip them) - // - // if this test fails, it means something it wrong with searching through the CLI - // specifically going to the previous/next search occurrence - let size = Size { cols: 80, rows: 10 }; - let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); - initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); - second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut mock_screen = MockScreen::new(size); - mock_screen.new_tab(second_tab_layout); - let session_metadata = mock_screen.clone_session_metadata(); - let screen_thread = mock_screen.run(Some(initial_layout)); - let received_server_instructions = Arc::new(Mutex::new(vec![])); - let server_receiver = mock_screen.server_receiver.take().unwrap(); - let server_thread = log_actions_in_thread!( - received_server_instructions, - ServerInstruction::KillSession, - server_receiver - ); - let update_search_term = CliAction::SearchInput { - input: "my-search-term".into(), - }; - let go_to_previous_search = CliAction::SearchPrevious; - let go_to_next_search = CliAction::SearchNext; - send_cli_action_to_server( - &session_metadata, - update_search_term, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - send_cli_action_to_server( - &session_metadata, - go_to_previous_search, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - send_cli_action_to_server( - &session_metadata, - go_to_next_search, - &mut mock_screen, - client_id, - ); - std::thread::sleep(std::time::Duration::from_millis(100)); - mock_screen.teardown(vec![server_thread, screen_thread]); - assert_snapshot!(format!( - "{:?}", - *received_server_instructions.lock().unwrap() - )); -} diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap index 0213fd278a..53beab4a13 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap @@ -3,4 +3,4 @@ source: zellij-server/src/./unit/screen_tests.rs assertion_line: 2674 expression: "format!(\"{:?}\", * received_server_instructions.lock().unwrap())" --- -[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] +[Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────────────────────────────────────────────┐\u{1b}[7;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────────────────────────────────────────────┘\u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[6;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}[2J\u{1b}[1;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #1 ─────────────────────────────┐\u{1b}[2;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;40H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;1H\u{1b}[m\u{1b}[38;5;0m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;2H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[1;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m┌ Pane #2 ─────────────────────────────┐\u{1b}[2;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[2;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[3;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[4;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[5;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[6;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[7;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[8;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[9;80H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m│\u{1b}[10;41H\u{1b}[m\u{1b}[39m\u{1b}[49m\u{1b}[29m\u{1b}[28m\u{1b}[27m\u{1b}[25m\u{1b}[25m\u{1b}[1m\u{1b}[24m\u{1b}[23m└──────────────────────────────────────┘\u{1b}[2;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[3;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[4;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[5;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[6;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[7;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[8;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}[9;42H\u{1b}[m\u{1b}[m\u{1b}]8;;\u{1b}\\ \u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[1;41H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), UnblockInputThread, Render(Some({1: "\u{1b}[?25l\u{1b}]0;Zellij (zellij-test) - Pane #1\u{7}\u{1b}[1;1H\u{1b}[m\u{1b}[?25h\u{1b}[2;2H\u{1b}[m\u{1b}[0 q"})), KillSession] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap new file mode 100644 index 0000000000..38b18ce746 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap @@ -0,0 +1,6 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +assertion_line: 1889 +expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +--- +[SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder") })), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap index 9718217bde..f2b264550b 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap @@ -1,16 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2258 -expression: "format!(\"{}\", snapshot)" +assertion_line: 2264 +expression: "format!(\"{}\", snapshot_count)" --- -00 (C): -01 (C): -02 (C): -03 (C): -04 (C): -05 (C): -06 (C): -07 (C): -08 (C): -09 (C): - +4 diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index c9e8cbbea9..24616bd17c 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -132,8 +132,6 @@ pub enum Sessions { direction: Option, #[clap(long, value_parser)] cwd: Option, - #[clap(short, long, value_parser)] - args: Option, #[clap(short, long, value_parser, default_missing_value("true"))] floating: Option, }, @@ -161,8 +159,6 @@ pub enum Sessions { #[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] pub enum CliAction { - /// Quit Zellij. - Quit, /// Write bytes to the terminal. Write { bytes: Vec }, /// Write characters to the terminal. @@ -214,8 +210,6 @@ pub enum CliAction { command: Option, #[clap(long, value_parser)] cwd: Option, - #[clap(short, long, value_parser)] - args: Option, #[clap(short, long, value_parser, default_missing_value("true"))] floating: Option, }, @@ -253,12 +247,6 @@ pub enum CliAction { RenameTab { name: String }, /// Remove a previously set tab name UndoRenameTab, - /// Search for String - SearchInput { input: String }, - /// Focus on next search occurrence - SearchNext, - /// Focus on previous search occurrence - SearchPrevious, /// Create a new tab, optionally with a specified tab layout and name NewTab { #[clap(short, long, value_parser)] diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index d44809110b..8d64472506 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -125,7 +125,7 @@ fn split_escaped_whitespace(s: &str) -> Vec { }) } -fn split_command_and_args(command: String, args: Option) -> (PathBuf, Vec) { +fn split_command_and_args(command: String) -> (PathBuf, Vec) { let mut full_command = split_escaped_whitespace(&command); let mut command = None; let mut command_args = vec![]; @@ -137,9 +137,6 @@ fn split_command_and_args(command: String, args: Option) -> (PathBuf, Ve } } let command = PathBuf::from(command.unwrap()); - if let Some(additional_args) = args.map(|args| split_escaped_whitespace(&args)).as_mut() { - command_args.append(additional_args); - } (command, command_args) } @@ -261,7 +258,6 @@ pub enum Action { impl Action { pub fn actions_from_cli(cli_action: CliAction) -> Result, String> { match cli_action { - CliAction::Quit => Ok(vec![Action::Quit]), CliAction::Write { bytes } => Ok(vec![Action::Write(bytes)]), CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]), CliAction::Resize { resize_direction } => Ok(vec![Action::Resize(resize_direction)]), @@ -288,11 +284,10 @@ impl Action { direction, command, cwd, - args, floating, } => match command { Some(command) => { - let (command, args) = split_command_and_args(command, args); + let (command, args) = split_command_and_args(command); let run_command_action = RunCommandAction { command, args, @@ -330,7 +325,10 @@ impl Action { CliAction::ToggleFloatingPanes => Ok(vec![Action::ToggleFloatingPanes]), CliAction::ClosePane => Ok(vec![Action::CloseFocus]), CliAction::RenamePane { name } => { - Ok(vec![Action::PaneNameInput(name.as_bytes().to_vec())]) + Ok(vec![ + Action::UndoRenamePane, + Action::PaneNameInput(name.as_bytes().to_vec()) + ]) }, CliAction::UndoRenamePane => Ok(vec![Action::UndoRenamePane]), CliAction::GoToNextTab => Ok(vec![Action::GoToNextTab]), @@ -342,17 +340,11 @@ impl Action { Action::TabNameInput(name.as_bytes().to_vec()), ]), CliAction::UndoRenameTab => Ok(vec![Action::UndoRenameTab]), - CliAction::SearchInput { input } => { - Ok(vec![Action::SearchInput(input.as_bytes().to_vec())]) - }, - CliAction::SearchNext => Ok(vec![Action::Search(SearchDirection::Down)]), - CliAction::SearchPrevious => Ok(vec![Action::Search(SearchDirection::Up)]), CliAction::NewTab { name, layout } => { if let Some(layout_path) = layout { let (path_to_raw_layout, raw_layout) = Layout::stringified_from_path_or_default(Some(&layout_path), None) .map_err(|e| format!("Failed to load layout: {}", e))?; - // let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| format!("Failed to parse layout: {}", e))?; let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| { let stringified_error = match e { ConfigError::KdlError(kdl_error) => { From 6e42d7a0ac819f2996eadd8ef8c3a014dc374fc4 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 11:38:24 +0200 Subject: [PATCH 47/55] style(fmt): rustfmt --- zellij-utils/src/input/actions.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 8d64472506..c547d940f5 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -324,12 +324,10 @@ impl Action { CliAction::TogglePaneEmbedOrFloating => Ok(vec![Action::TogglePaneEmbedOrFloating]), CliAction::ToggleFloatingPanes => Ok(vec![Action::ToggleFloatingPanes]), CliAction::ClosePane => Ok(vec![Action::CloseFocus]), - CliAction::RenamePane { name } => { - Ok(vec![ - Action::UndoRenamePane, - Action::PaneNameInput(name.as_bytes().to_vec()) - ]) - }, + CliAction::RenamePane { name } => Ok(vec![ + Action::UndoRenamePane, + Action::PaneNameInput(name.as_bytes().to_vec()), + ]), CliAction::UndoRenamePane => Ok(vec![Action::UndoRenamePane]), CliAction::GoToNextTab => Ok(vec![Action::GoToNextTab]), CliAction::GoToPreviousTab => Ok(vec![Action::GoToPreviousTab]), From e9ad50d5036bd8f19897e7ab68f0fce44cb74416 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 16:29:22 +0200 Subject: [PATCH 48/55] fix(themes): loading of theme files --- zellij-utils/src/kdl/mod.rs | 47 ++++++++++++++++++------------------- zellij-utils/src/setup.rs | 9 +++++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 8743412e94..1c8079c2c8 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1569,35 +1569,34 @@ impl Themes { impl Theme { pub fn from_path(path_to_theme_file: PathBuf) -> Result<(String, Self), ConfigError> { // String is the theme name - let mut file = File::open(path_to_theme_file)?; + let mut file = File::open(path_to_theme_file.clone())?; let mut kdl_config = String::new(); file.read_to_string(&mut kdl_config)?; let kdl_config: KdlDocument = kdl_config.parse()?; - let kdl_config = kdl_config.nodes().get(0).ok_or(ConfigError::new_kdl_error( - "No theme found in file".into(), + 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 theme_name = kdl_name!(kdl_config); - let theme_colors = kdl_children_or_error!(kdl_config, "empty theme"); - Ok(( - theme_name.into(), - Theme { - palette: Palette { - fg: PaletteColor::try_from(("fg", theme_colors))?, - bg: PaletteColor::try_from(("bg", theme_colors))?, - red: PaletteColor::try_from(("red", theme_colors))?, - green: PaletteColor::try_from(("green", theme_colors))?, - yellow: PaletteColor::try_from(("yellow", theme_colors))?, - blue: PaletteColor::try_from(("blue", theme_colors))?, - magenta: PaletteColor::try_from(("magenta", theme_colors))?, - orange: PaletteColor::try_from(("orange", theme_colors))?, - cyan: PaletteColor::try_from(("cyan", theme_colors))?, - black: PaletteColor::try_from(("black", theme_colors))?, - white: PaletteColor::try_from(("white", theme_colors))?, - ..Default::default() - }, - }, - )) + let all_themes_in_file = Themes::from_kdl(kdl_themes)?; + let theme_file_name = path_to_theme_file.file_name().ok_or(ConfigError::new_kdl_error( + "Failed to find file name".into(), + kdl_config.span().offset(), + kdl_config.span().len(), + ))?.to_string_lossy().to_string(); + if let Some(theme_name) = theme_file_name.strip_suffix(".kdl") { + let theme = all_themes_in_file.get_theme(theme_name).ok_or(ConfigError::new_kdl_error( + format!("Not theme with name {} found in file {:?}", theme_name, path_to_theme_file), + kdl_config.span().offset(), + kdl_config.span().len(), + ))?; + Ok((theme_name.to_string(), theme.clone())) + } else { + Err(ConfigError::new_kdl_error( + "no theme file found".into(), + kdl_config.span().offset(), + kdl_config.span().len(), + )) + } } } diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index abd235eab3..c8d4dae8b0 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -229,8 +229,13 @@ impl Setup { for entry in (theme_dir.read_dir()?).flatten() { if let Some(extension) = entry.path().extension() { if extension == "kdl" { - if let Ok((theme_name, theme)) = Theme::from_path(entry.path()) { - config.themes.insert(theme_name, theme); + match Theme::from_path(entry.path()) { + Ok((theme_name, theme)) => { + config.themes.insert(theme_name, theme); + }, + Err(e) => { + log::error!("error loading theme file: {:?}", e); + } } } } From a1d785345a4cff720de7d982baadc6bac22e2215 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 16:29:38 +0200 Subject: [PATCH 49/55] style(fmt): rustfmt --- zellij-utils/src/kdl/mod.rs | 28 +++++++++++++++++++--------- zellij-utils/src/setup.rs | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 1c8079c2c8..0511ac8206 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1579,17 +1579,27 @@ impl Theme { kdl_config.span().len(), ))?; let all_themes_in_file = Themes::from_kdl(kdl_themes)?; - let theme_file_name = path_to_theme_file.file_name().ok_or(ConfigError::new_kdl_error( - "Failed to find file name".into(), - kdl_config.span().offset(), - kdl_config.span().len(), - ))?.to_string_lossy().to_string(); - if let Some(theme_name) = theme_file_name.strip_suffix(".kdl") { - let theme = all_themes_in_file.get_theme(theme_name).ok_or(ConfigError::new_kdl_error( - format!("Not theme with name {} found in file {:?}", theme_name, path_to_theme_file), + let theme_file_name = path_to_theme_file + .file_name() + .ok_or(ConfigError::new_kdl_error( + "Failed to find file name".into(), kdl_config.span().offset(), kdl_config.span().len(), - ))?; + ))? + .to_string_lossy() + .to_string(); + if let Some(theme_name) = theme_file_name.strip_suffix(".kdl") { + let theme = + all_themes_in_file + .get_theme(theme_name) + .ok_or(ConfigError::new_kdl_error( + format!( + "Not theme with name {} found in file {:?}", + theme_name, path_to_theme_file + ), + kdl_config.span().offset(), + kdl_config.span().len(), + ))?; Ok((theme_name.to_string(), theme.clone())) } else { Err(ConfigError::new_kdl_error( diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index c8d4dae8b0..9972915e4f 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -235,7 +235,7 @@ impl Setup { }, Err(e) => { log::error!("error loading theme file: {:?}", e); - } + }, } } } From 03c13336cbf4a2765594adcda171fb77009c84ea Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 16:40:26 +0200 Subject: [PATCH 50/55] fix(tests): theme fixtures --- .../input/unit/fixtures/themes/dracula.kdl | 28 ++++++++++--------- .../src/input/unit/fixtures/themes/nord.kdl | 26 +++++++++-------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl b/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl index 2201b73ac9..77a25f4b51 100644 --- a/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl +++ b/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl @@ -1,16 +1,18 @@ // Dracula Theme -dracula { - // From https://github.com/dracula/zellij - 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 - fg 248 248 242 - cyan 139 233 253 - black 0 0 0 - white 255 255 255 +themes { + dracula { + // From https://github.com/dracula/zellij + 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 + fg 248 248 242 + cyan 139 233 253 + black 0 0 0 + white 255 255 255 + } } diff --git a/zellij-utils/src/input/unit/fixtures/themes/nord.kdl b/zellij-utils/src/input/unit/fixtures/themes/nord.kdl index 136b983992..361a21fd9b 100644 --- a/zellij-utils/src/input/unit/fixtures/themes/nord.kdl +++ b/zellij-utils/src/input/unit/fixtures/themes/nord.kdl @@ -1,15 +1,17 @@ // Nord theme -nord { - fg 216 222 233 //#D8DEE9 - bg 46 52 64 //#2E3440 - black 59 66 82 //#3B4252 - red 191 97 106 //#BF616A - green 163 190 140 //#A3BE8C - yellow 235203139 //#EBCB8B - blue 129 161 193 //#81A1C1 - magenta 180 142 173 //#B48EAD - cyan 136 192 208 //#88C0D0 - white 229 233 240 //#E5E9F0 - orange 208 135 112 //#D08770 +themes { + nord { + fg 216 222 233 //#D8DEE9 + bg 46 52 64 //#2E3440 + black 59 66 82 //#3B4252 + red 191 97 106 //#BF616A + green 163 190 140 //#A3BE8C + yellow 235203139 //#EBCB8B + blue 129 161 193 //#81A1C1 + magenta 180 142 173 //#B48EAD + cyan 136 192 208 //#88C0D0 + white 229 233 240 //#E5E9F0 + orange 208 135 112 //#D08770 + } } From 699d058c7995443fea7ecd7ae9ed796bf4b81a18 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 17:47:13 +0200 Subject: [PATCH 51/55] fix(layouts): better errors on unknown nodes --- ...error_on_unknown_layout_pane_property.snap | 4 +- ...unknown_layout_pane_template_property.snap | 4 +- zellij-utils/src/kdl/kdl_layout_parser.rs | 59 ++++++++++++++++--- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap index b856564086..573ca75a1a 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 903 +assertion_line: 926 expression: "format!(\"{:?}\", layout_error)" --- -KdlError(KdlError { error_message: "Invalid pane property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(16) }) +KdlError(KdlError { error_message: "Unknown pane property: spit_size", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(35), len: Some(11) }) diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap index 404f988cd2..09701ebe46 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 914 +assertion_line: 939 expression: "format!(\"{:?}\", layout_error)" --- -KdlError(KdlError { error_message: "Invalid pane property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(30), len: Some(49) }) +KdlError(KdlError { error_message: "Unknown pane property: spit_size", src: Some(NamedSource { name: "layout_file_name", source: ""), offset: Some(68), len: Some(11) }) diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 93e7e8b17f..cc2eedc730 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -67,6 +67,8 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "cwd" || property_name == "args" || property_name == "split_direction" + || property_name == "pane" + || property_name == "children" } fn is_a_valid_tab_property(&self, property_name: &str) -> bool { property_name == "focus" || property_name == "name" || property_name == "split_direction" @@ -362,6 +364,18 @@ impl<'a> KdlLayoutParser<'a> { pane_template, &pane_template_kdl_node, )?); + } else if self.is_a_valid_tab_property(kdl_name!(child)) { + return Err(ConfigError::new_kdl_error( + format!("Tab property '{}' must be placed on the tab title line and not in the child braces", kdl_name!(child)), + child.span().offset(), + child.span().len() + )); + } else { + return Err(ConfigError::new_kdl_error( + format!("Invalid tab property: {}", kdl_name!(child)), + child.span().offset(), + child.span().len() + )); } } if nodes.is_empty() { @@ -389,6 +403,12 @@ impl<'a> KdlLayoutParser<'a> { pane_template, &pane_template_kdl_node, )?); + } else if !self.is_a_valid_pane_property(kdl_name!(child)) { + return Err(ConfigError::new_kdl_error( + format!("Unknown pane property: {}", kdl_name!(child)), + child.span().offset(), + child.span().len(), + )); } } Ok((external_children_index, nodes)) @@ -405,14 +425,25 @@ impl<'a> KdlLayoutParser<'a> { Ok(()) } fn assert_valid_pane_properties(&self, pane_node: &KdlNode) -> Result<(), ConfigError> { - let all_property_names = kdl_property_names!(pane_node); - for name in all_property_names { - if !self.is_a_valid_pane_property(name) { - return Err(ConfigError::new_kdl_error( - format!("Invalid pane property '{}'", name), - pane_node.span().offset(), - pane_node.span().len(), - )); + for entry in pane_node.entries() { + match entry.name().map(|e| e.value()).or_else(|| entry.value().as_string()) { + Some(string_name) => { + if !self.is_a_valid_pane_property(string_name) { + return Err(ConfigError::new_kdl_error( + format!("Unknown pane property: {}", string_name), + entry.span().offset(), + entry.span().len(), + )); + } + }, + None => { + return Err(ConfigError::new_kdl_error( + "Unknown pane property".into(), + entry.span().offset(), + entry.span().len(), + )); + + } } } Ok(()) @@ -530,6 +561,18 @@ impl<'a> KdlLayoutParser<'a> { pane_template, &pane_template_kdl_node, )?); + } else if self.is_a_valid_tab_property(kdl_name!(child)) { + return Err(ConfigError::new_kdl_error( + format!("Tab property '{}' must be placed on the tab_template title line and not in the child braces", kdl_name!(child)), + child.span().offset(), + child.span().len() + )); + } else { + return Err(ConfigError::new_kdl_error( + format!("Invalid tab_template property: {}", kdl_name!(child)), + child.span().offset(), + child.span().len() + )); } } } From 35d5612bfd4293d538e2a5c207d800928a300293 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 18:07:52 +0200 Subject: [PATCH 52/55] fix(kdl): clarify valid node terminator error --- zellij-utils/src/input/actions.rs | 6 +++++- zellij-utils/src/input/config.rs | 6 +++++- zellij-utils/src/kdl/kdl_layout_parser.rs | 13 ++++++++----- zellij-utils/src/kdl/mod.rs | 18 ++++++++++++++++-- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index c547d940f5..8dacdc6527 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -353,7 +353,11 @@ impl Action { ConfigError::KdlDeserializationError(kdl_error) => { let error_message = match kdl_error.kind { kdl::KdlErrorKind::Context("valid node terminator") => { - format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") + format!("Failed to deserialize KDL node. \nPossible reasons:\n{}\n{}\n{}\n{}", + "- Missing `;` after a node name, eg. { node; another_node; }", + "- Missing quotations (\") around an argument node eg. { first_node \"argument_node\"; }", + "- Missing an equal sign (=) between node arguments on a title line. eg. argument=\"value\"", + "- Found an extraneous equal sign (=) between node child arguments and their values. eg. { argument=\"value\" }") }, _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), }; diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 2274d25191..ca161c94bc 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -186,7 +186,11 @@ impl Config { Err(ConfigError::KdlDeserializationError(kdl_error)) => { let error_message = match kdl_error.kind { kdl::KdlErrorKind::Context("valid node terminator") => { - format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") + format!("Failed to deserialize KDL node. \nPossible reasons:\n{}\n{}\n{}\n{}", + "- Missing `;` after a node name, eg. { node; another_node; }", + "- Missing quotations (\") around an argument node eg. { first_node \"argument_node\"; }", + "- Missing an equal sign (=) between node arguments on a title line. eg. argument=\"value\"", + "- Found an extraneous equal sign (=) between node child arguments and their values. eg. { argument=\"value\" }") }, _ => { String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")) diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index cc2eedc730..4bf3aea606 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -374,7 +374,7 @@ impl<'a> KdlLayoutParser<'a> { return Err(ConfigError::new_kdl_error( format!("Invalid tab property: {}", kdl_name!(child)), child.span().offset(), - child.span().len() + child.span().len(), )); } } @@ -426,7 +426,11 @@ impl<'a> KdlLayoutParser<'a> { } fn assert_valid_pane_properties(&self, pane_node: &KdlNode) -> Result<(), ConfigError> { for entry in pane_node.entries() { - match entry.name().map(|e| e.value()).or_else(|| entry.value().as_string()) { + match entry + .name() + .map(|e| e.value()) + .or_else(|| entry.value().as_string()) + { Some(string_name) => { if !self.is_a_valid_pane_property(string_name) { return Err(ConfigError::new_kdl_error( @@ -442,8 +446,7 @@ impl<'a> KdlLayoutParser<'a> { entry.span().offset(), entry.span().len(), )); - - } + }, } } Ok(()) @@ -571,7 +574,7 @@ impl<'a> KdlLayoutParser<'a> { return Err(ConfigError::new_kdl_error( format!("Invalid tab_template property: {}", kdl_name!(child)), child.span().offset(), - child.span().len() + child.span().len(), )); } } diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 0511ac8206..7092653c66 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -201,6 +201,17 @@ macro_rules! kdl_children_nodes { }; } +#[macro_export] +macro_rules! kdl_property_nodes { + ( $kdl_node:expr ) => {{ + $kdl_node + .entries() + .iter() + .filter_map(|e| e.name()) + .map(|e| e.value()) + }}; +} + #[macro_export] macro_rules! kdl_children_or_error { ( $kdl_node:expr, $error:expr ) => { @@ -252,7 +263,6 @@ macro_rules! kdl_property_names { macro_rules! kdl_argument_values { ( $kdl_node:expr ) => { $kdl_node.entries().iter().collect() - // $kdl_node.entries().iter().map(|arg| arg.value()).collect() }; } @@ -1264,7 +1274,11 @@ impl Layout { ConfigError::KdlDeserializationError(kdl_error) => { let error_message = match kdl_error.kind { kdl::KdlErrorKind::Context("valid node terminator") => { - format!("Missing `;`, a valid line ending or an equal sign `=` between property and value (eg. foo=\"bar\")") + format!("Failed to deserialize KDL node. \nPossible reasons:\n{}\n{}\n{}\n{}", + "- Missing `;` after a node name, eg. { node; another_node; }", + "- Missing quotations (\") around an argument node eg. { first_node \"argument_node\"; }", + "- Missing an equal sign (=) between node arguments on a title line. eg. argument=\"value\"", + "- Found an extraneous equal sign (=) between node child arguments and their values. eg. { argument=\"value\" }") }, _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), }; From 9d0b773055212ff71ea79f12acb6fc7d43542203 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 23:13:50 +0200 Subject: [PATCH 53/55] fix(e2e): adjust close tab test --- src/tests/e2e/cases.rs | 1 - .../e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index c2975d168f..1f8deb5aaf 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -460,7 +460,6 @@ pub fn close_tab() { let mut step_is_complete = false; if remote_terminal.cursor_position_is(3, 2) && !remote_terminal.snapshot_contains("Tab #2") - && remote_terminal.tip_appears() { // cursor is in the first tab again step_is_complete = true; diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap index 5f757fb657..b4ad5a98dd 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 478 +assertion_line: 482 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. + <←→> Move focus / New / Close / Rename / Sync / Toggle / Select pane From 094ff217687eed0311c951d8b5dc7e84f08752e3 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 3 Oct 2022 23:37:13 +0200 Subject: [PATCH 54/55] fix(e2e): adjust close tab test again --- src/tests/e2e/cases.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 1f8deb5aaf..071262c443 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -458,7 +458,7 @@ pub fn close_tab() { name: "Wait for tab to close", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) + if remote_terminal.snapshot_contains("Tab #1") && !remote_terminal.snapshot_contains("Tab #2") { // cursor is in the first tab again @@ -474,7 +474,8 @@ pub fn close_tab() { break last_snapshot; } }; - assert_snapshot!(last_snapshot); + assert!(last_snapshot.contains("Tab #1")); + assert!(!last_snapshot.contains("Tab #2")); } #[test] From 52eec6c63ade29a82c0d9ea50fb5666b3b208e7c Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Tue, 4 Oct 2022 09:32:00 +0200 Subject: [PATCH 55/55] style(code): cleanup some comments --- src/commands.rs | 6 ++-- src/tests/e2e/cases.rs | 72 ---------------------------------------- zellij-client/src/lib.rs | 7 ---- zellij-server/src/pty.rs | 15 --------- 4 files changed, 4 insertions(+), 96 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index db6e94d726..b4e2e1da2c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -390,7 +390,6 @@ pub(crate) fn start_client(opts: CliArgs) { Some(layout), ); } else { - // TODO: bring this back if let Some(session_name) = config_options.session_name.as_ref() { match config_options.attach_to_session { Some(true) => { @@ -424,7 +423,10 @@ pub(crate) fn start_client(opts: CliArgs) { ); }, } - process::exit(0); // TODO: why is this here? + // after we detach, this happens and so we need to exit before the rest of the + // function happens + // TODO: offload this to a different function + process::exit(0); } let session_name = names::Generator::default().next().unwrap(); diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 071262c443..63e2d67789 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -948,41 +948,6 @@ pub fn detach_and_attach_session() { assert_snapshot!(last_snapshot); } -// #[test] -// #[ignore] -// pub fn accepts_basic_layout() { -// let fake_win_size = Size { -// cols: 120, -// rows: 24, -// }; -// let layout_file_name = "three-panes-with-nesting.yaml"; -// let mut test_attempts = 10; -// let last_snapshot = loop { -// RemoteRunner::kill_running_sessions(fake_win_size); -// let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); -// runner.run_all_steps(); -// let last_snapshot = runner.take_snapshot_after(Step { -// name: "Wait for app to load", -// instruction: |remote_terminal: RemoteTerminal| -> bool { -// let mut step_is_complete = false; -// if remote_terminal.cursor_position_is(3, 1) -// && remote_terminal.snapshot_contains("$ █ ││$") -// && remote_terminal.snapshot_contains("$ ") { -// step_is_complete = true; -// } -// step_is_complete -// }, -// }); -// if runner.test_timed_out && test_attempts > 0 { -// test_attempts -= 1; -// continue; -// } else { -// break last_snapshot; -// } -// }; -// assert_snapshot!(last_snapshot); -// } - #[test] #[ignore] pub fn status_bar_loads_custom_keybindings() { @@ -1718,43 +1683,6 @@ pub fn toggle_floating_panes() { assert_snapshot!(last_snapshot); } -// #[test] -// #[ignore] -// pub fn focus_tab_with_layout() { -// let fake_win_size = Size { -// cols: 120, -// rows: 24, -// }; -// let layout_file_name = "focus-tab-layout.yaml"; -// let mut test_attempts = 10; -// let last_snapshot = loop { -// RemoteRunner::kill_running_sessions(fake_win_size); -// let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name); -// runner.run_all_steps(); -// let last_snapshot = runner.take_snapshot_after(Step { -// name: "Wait for app to load", -// instruction: |remote_terminal: RemoteTerminal| -> bool { -// let mut step_is_complete = false; -// if remote_terminal.status_bar_appears() -// && remote_terminal.tip_appears() -// && remote_terminal.snapshot_contains("Tab #9") -// && remote_terminal.cursor_position_is(63, 2) -// { -// step_is_complete = true; -// } -// step_is_complete -// }, -// }); -// if runner.test_timed_out && test_attempts > 0 { -// test_attempts -= 1; -// continue; -// } else { -// break last_snapshot; -// } -// }; -// assert_snapshot!(last_snapshot); -// } - #[test] #[ignore] pub fn tmux_mode() { diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 287312f8b8..bac08715f6 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -143,13 +143,6 @@ pub fn start_client( envs::set_zellij("0".to_string()); config.env.set_vars(); - // let palette = config.themes.clone().map_or_else( - // || os_input.load_palette(), - // |t| { - // t.theme_config(&config_options) - // .unwrap_or_else(|| os_input.load_palette()) - // }, - // ); let palette = config .theme_config(&config_options) .unwrap_or_else(|| os_input.load_palette()); diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 43fa70112c..df4d270aa5 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -33,7 +33,6 @@ pub(crate) enum PtyInstruction { SpawnTerminalHorizontally(Option, ClientId), UpdateActivePane(Option, ClientId), GoToTab(TabIndex, ClientId), - // NewTab(Option, Option, ClientId), NewTab( Option, Option, @@ -141,20 +140,6 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) { .unwrap(); }, PtyInstruction::NewTab(terminal_action, tab_layout, tab_name, client_id) => { - // let tab_name = tab_layout.as_ref().and_then(|layout| { - // if layout.name.is_empty() { - // None - // } else { - // Some(layout.name.clone()) - // } - // }); - - // let merged_layout = layout.template.clone().insert_tab_layout(tab_layout); - // let mut layout = layout.clone(); - // if let Some(tab_layout) = tab_layout { - // layout.insert_tab_layout(&tab_layout).expect("corrupted tab layout"); - // } - pty.spawn_terminals_for_layout( tab_layout.unwrap_or_else(|| layout.new_tab()), terminal_action.clone(),