aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-10-05 07:44:00 +0200
committerGitHub <noreply@github.com>2022-10-05 07:44:00 +0200
commit79bf6ab868cbdab1f9a3827c9b70198f54548b44 (patch)
tree2d6fc4c1d8a79ebd727a1a5f8b6406617dd0de55
parentdocs(changelog): add darwin dependencies (diff)
downloadsl0ppij-79bf6ab868cbdab1f9a3827c9b70198f54548b44.tar.gz
sl0ppij-79bf6ab868cbdab1f9a3827c9b70198f54548b44.tar.xz
sl0ppij-79bf6ab868cbdab1f9a3827c9b70198f54548b44.zip
feat(config): switch to kdl (#1759)
* chore(config): default kdl keybindings config * tests * work * refactor(config): move stuff around * work * tab merge layout * work * work * layouts working * work * layout tests * work * work * feat(parsing): kdl layouts without config * refactor(kdl): move stuff around * work * tests(layout): add cases and fix bugs * work * fix(kdl): various bugs * chore(layouts): move all layouts to kdl * feat(kdl): shared keybidns * fix(layout): do not count fixed panes toward percentile * fix(keybinds): missing keybinds and actions * fix(config): adjust default tips * refactor(config): move stuff around * fix(tests): make e2e tests pass * fix(kdl): add verbose parsing errors * fix(kdl): focused tab * fix(layout): corret default_tab_template behavior * style(code): fix compile warnings * feat(cli): send actions through the cli * fix(cli): exit only when action is done * fix(cli): open embedded pane from floating pane * fix(cli): send actions to other sessions * feat(cli): command alias * feat(converter): convert old config * feat(converter): convert old layout and theme files * feat(kdl): pretty errors * feat(client): convert old YAML files on startup * fix: various bugs and styling issues * fix: e2e tests * fix(screen): propagate errors after merge * style(clippy): lower clippy level * fix(tests): own session_name variable * style(fmt): rustfmt * fix(cli): various action fixes * style(fmt): rustfmt * fix(themes): loading of theme files * style(fmt): rustfmt * fix(tests): theme fixtures * fix(layouts): better errors on unknown nodes * fix(kdl): clarify valid node terminator error * fix(e2e): adjust close tab test * fix(e2e): adjust close tab test again * style(code): cleanup some comments
-rw-r--r--Cargo.lock65
-rw-r--r--Cargo.toml1
-rw-r--r--Makefile.toml2
-rw-r--r--default-plugins/status-bar/src/second_line.rs10
-rw-r--r--default-plugins/status-bar/src/tip/data/quicknav.rs7
-rw-r--r--example/themes/README.md4
-rw-r--r--example/themes/dracula.kdl17
-rw-r--r--example/themes/dracula.yaml16
-rw-r--r--example/themes/gruvbox-dark.kdl16
-rw-r--r--example/themes/gruvbox-dark.yaml15
-rw-r--r--example/themes/gruvbox-light.kdl16
-rw-r--r--example/themes/gruvbox-light.yaml15
-rw-r--r--example/themes/molokai-dark.kdl16
-rw-r--r--example/themes/molokai-dark.yaml15
-rw-r--r--example/themes/nord.kdl15
-rw-r--r--example/themes/nord.yaml16
-rw-r--r--example/themes/one-half-dark.kdl16
-rw-r--r--example/themes/one-half-dark.yaml15
-rw-r--r--example/themes/solarized-dark.kdl16
-rw-r--r--example/themes/solarized-dark.yaml15
-rw-r--r--example/themes/solarized-light.kdl16
-rw-r--r--example/themes/solarized-light.yaml15
-rw-r--r--example/themes/tokyo-night-dark.kdl0
-rw-r--r--example/themes/tokyo-night-light.kdl16
-rw-r--r--example/themes/tokyo-night-light.yaml16
-rw-r--r--example/themes/tokyo-night-storm.kdl16
-rw-r--r--example/themes/tokyo-night-storm.yaml16
-rw-r--r--example/themes/tokyo-night.kdl16
-rw-r--r--example/themes/tokyo-night.yaml16
-rw-r--r--src/commands.rs214
-rw-r--r--src/main.rs51
-rw-r--r--src/tests/e2e/cases.rs80
-rw-r--r--src/tests/e2e/remote_runner.rs56
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__bracketed_paste.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__typing_exit_closes_pane.snap2
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_pane.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__undo_rename_tab.snap4
-rw-r--r--src/tests/fixtures/configs/changed_keys.kdl12
-rw-r--r--src/tests/fixtures/configs/changed_keys.yaml26
-rw-r--r--src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml19
-rw-r--r--src/tests/fixtures/layouts/parts-total-more-than-100-percent.yaml20
-rw-r--r--src/tests/fixtures/layouts/three-panes-with-nesting.yaml17
-rw-r--r--zellij-client/Cargo.toml3
-rw-r--r--zellij-client/src/cli_client.rs34
-rw-r--r--zellij-client/src/fake_client.rs184
-rw-r--r--zellij-client/src/input_handler.rs92
-rw-r--r--zellij-client/src/lib.rs20
-rw-r--r--zellij-client/src/old_config_converter/convert_old_yaml_files.rs332
-rw-r--r--zellij-client/src/old_config_converter/mod.rs6
-rw-r--r--zellij-client/src/old_config_converter/old_config.rs1292
-rw-r--r--zellij-client/src/old_config_converter/old_layout.rs557
-rw-r--r--zellij-client/src/old_config_converter/unit/convert_config_tests.rs126
-rw-r--r--zellij-client/src/old_config_converter/unit/convert_layout_tests.rs142
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml (renamed from src/tests/fixtures/layouts/focus-tab-layout.yaml)2
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout_htop_command.yaml93
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml (renamed from zellij-utils/assets/config/default.yaml)4
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml (renamed from zellij-utils/assets/layouts/default.yaml)0
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_custom_options.yaml639
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_env_variables.yaml644
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_global_keybind_unbinds.yaml639
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_themes.yaml677
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_ui.yaml643
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbind_all_keys_per_mode.yaml639
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_config_with_unbinds_in_mode.yaml640
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config.yaml34
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_config_and_session_name.yaml36
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml (renamed from zellij-utils/assets/layouts/strider.yaml)10
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml (renamed from zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml)19
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml (renamed from zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml)14
-rw-r--r--zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout_with_plugins.yaml35
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_custom_options.snap411
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_env_variables.snap415
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_global_keybind_unbinds.snap411
-rw-r--r--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.snap411
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_themes_config.snap451
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__convert_config_with_ui_config.snap416
-rw-r--r--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.snap410
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_config__convert_config_test__properly_convert_default_config.snap410
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_default_layout.snap14
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_1.snap61
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_2.snap60
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_3.snap19
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_example_4.snap27
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_config.snap24
-rw-r--r--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.snap26
-rw-r--r--zellij-client/src/old_config_converter/unit/snapshots/zellij_client__old_config_converter__old_layout__convert_layout_test__properly_convert_layout_with_session_name.snap16
-rw-r--r--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.snap16
-rw-r--r--zellij-client/src/sessions.rs18
-rw-r--r--zellij-server/src/lib.rs45
-rw-r--r--zellij-server/src/panes/terminal_pane.rs1
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs47
-rw-r--r--zellij-server/src/panes/tiled_panes/pane_resizer.rs22
-rw-r--r--zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs75
-rw-r--r--zellij-server/src/pty.rs35
-rw-r--r--zellij-server/src/route.rs113
-rw-r--r--zellij-server/src/screen.rs690
-rw-r--r--zellij-server/src/tab/mod.rs22
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_basic_layout.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_layout.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__tab_with_nested_uneven_layout.snap26
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs173
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs17
-rw-r--r--zellij-server/src/thread_bus.rs6
-rw-r--r--zellij-server/src/unit/fixtures/layout-with-three-panes.kdl5
-rw-r--r--zellij-server/src/unit/screen_tests.rs1953
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_pane_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-6.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action-7.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_close_tab_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_dump_screen_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-2.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-3.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_next_pane_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-2.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-3.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_focus_previous_pane_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_go_to_next_and_previous_search_occurrence.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-6.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action-7.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_goto_tab_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action-6.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_down_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_half_page_scroll_up_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-2.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-3.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_or_tab_pane_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-2.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-3.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_focus_pane_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-2.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-3.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_move_pane_action.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_cwd_and_args.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap55
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-6.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action-7.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_next_tab_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action-6.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_down_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_page_scroll_up_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-6.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action-7.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_previous_tab_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_pane_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap436
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-2.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen-3.snap26
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_resize_action_to_screen.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-6.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-7.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-8.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action-9.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_down_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-6.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-7.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action-8.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_to_bottom_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action-5.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_scroll_up_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_switch_mode_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-5.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes-6.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_floating_panes.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_full_screen_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float-5.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_embed_or_float.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action-4.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_pane_frames_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-2.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-3.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-4.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-5.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-6.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action.snap16
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap473
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_update_search_term.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap6
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap6
-rw-r--r--zellij-utils/Cargo.toml4
-rw-r--r--zellij-utils/assets/config/default.kdl294
-rw-r--r--zellij-utils/assets/layouts/compact.kdl6
-rw-r--r--zellij-utils/assets/layouts/compact.yaml13
-rw-r--r--zellij-utils/assets/layouts/default.kdl9
-rw-r--r--zellij-utils/assets/layouts/disable-status-bar.kdl6
-rw-r--r--zellij-utils/assets/layouts/disable-status-bar.yaml13
-rw-r--r--zellij-utils/assets/layouts/no-plugins.kdl1
-rw-r--r--zellij-utils/assets/layouts/no-plugins.yaml8
-rw-r--r--zellij-utils/assets/layouts/strider.kdl19
-rw-r--r--zellij-utils/src/cli.rs146
-rw-r--r--zellij-utils/src/data.rs162
-rw-r--r--zellij-utils/src/envs.rs24
-rw-r--r--zellij-utils/src/errors.rs4
-rw-r--r--zellij-utils/src/input/actions.rs262
-rw-r--r--zellij-utils/src/input/config.rs681
-rw-r--r--zellij-utils/src/input/keybinds.rs336
-rw-r--r--zellij-utils/src/input/layout.rs849
-rw-r--r--zellij-utils/src/input/mod.rs1
-rw-r--r--zellij-utils/src/input/options.rs35
-rw-r--r--zellij-utils/src/input/plugins.rs191
-rw-r--r--zellij-utils/src/input/theme.rs193
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml41
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml18
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/no-layout-template-specified.yaml6
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/no-tab-section-specified.yaml6
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/session-name-to-layout.yaml3
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml35
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml29
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/dracula.kdl18
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/dracula.yaml16
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/nord.kdl17
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/nord.yaml16
-rw-r--r--zellij-utils/src/input/unit/keybinds_test.rs1244
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs1646
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap139
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap147
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap201
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap231
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap125
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap94
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap239
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap96
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__layout_test__dracula_theme_from_file.snap109
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap109
-rw-r--r--zellij-utils/src/input/unit/theme_test.rs15
-rw-r--r--zellij-utils/src/ipc.rs8
-rw-r--r--zellij-utils/src/kdl/kdl_layout_parser.rs888
-rw-r--r--zellij-utils/src/kdl/mod.rs1626
-rw-r--r--zellij-utils/src/lib.rs5
-rw-r--r--zellij-utils/src/pane_size.rs23
-rw-r--r--zellij-utils/src/setup.rs952
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap27
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap21
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap27
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap82
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap25
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap3363
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap3367
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap149
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap21
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap27
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap3377
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap3667
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap3363
-rw-r--r--zellij-utils/src/test-fixtures/config-with-env-vars.kdl4
-rw-r--r--zellij-utils/src/test-fixtures/config-with-keybindings-config.kdl11
-rw-r--r--zellij-utils/src/test-fixtures/config-with-plugins-config.kdl6
-rw-r--r--zellij-utils/src/test-fixtures/config-with-themes-config.kdl28
-rw-r--r--zellij-utils/src/test-fixtures/config-with-ui-config.kdl5
-rw-r--r--zellij-utils/src/test-fixtures/layout-with-env-vars.kdl5
-rw-r--r--zellij-utils/src/test-fixtures/layout-with-keybindings-config.kdl11
-rw-r--r--zellij-utils/src/test-fixtures/layout-with-options.kdl2
-rw-r--r--zellij-utils/src/test-fixtures/layout-with-plugins-config.kdl5
-rw-r--r--zellij-utils/src/test-fixtures/layout-with-themes-config.kdl30
-rw-r--r--zellij-utils/src/test-fixtures/layout-with-ui-config.kdl6
355 files changed, 42490 insertions, 4776 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8130b6aa..ad8705d6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1226,6 +1226,17 @@ dependencies = [
]
[[package]]
+name = "kdl"
+version = "4.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8388a371e0e2ede18bbd94e476fcd45b4ac65cefcedf0c06fd13bd8389574a6"
+dependencies = [
+ "miette 5.3.0",
+ "nom 7.1.1",
+ "thiserror",
+]
+
+[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1412,7 +1423,7 @@ checksum = "cd2adcfcced5d625bf90a958a82ae5b93231f57f3df1383fee28c9b5096d35ed"
dependencies = [
"atty",
"backtrace",
- "miette-derive",
+ "miette-derive 3.3.0",
"once_cell",
"owo-colors",
"supports-color",
@@ -1424,6 +1435,18 @@ dependencies = [
]
[[package]]
+name = "miette"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d"
+dependencies = [
+ "miette-derive 5.3.0",
+ "once_cell",
+ "thiserror",
+ "unicode-width",
+]
+
+[[package]]
name = "miette-derive"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1435,6 +1458,23 @@ dependencies = [
]
[[package]]
+name = "miette-derive"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f2485ed7d1fe80704928e3eb86387439609bd0c6bb96db8208daa364cfd1e09"
+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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1504,6 +1544,16 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2501,7 +2551,7 @@ checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e"
dependencies = [
"dirs",
"fnv",
- "nom",
+ "nom 5.1.2",
"phf",
"phf_codegen",
]
@@ -3312,11 +3362,12 @@ dependencies = [
"dialoguer",
"insta",
"log",
- "miette",
+ "miette 3.3.0",
"names",
"rand 0.8.5",
"ssh2",
"suggest",
+ "thiserror",
"zellij-client",
"zellij-server",
"zellij-utils",
@@ -3329,6 +3380,9 @@ dependencies = [
"insta",
"log",
"mio",
+ "serde",
+ "serde_yaml",
+ "url",
"zellij-utils",
]
@@ -3393,19 +3447,20 @@ dependencies = [
"colorsys",
"crossbeam",
"directories-next",
+ "insta",
"interprocess",
+ "kdl",
"lazy_static",
"libc",
"log",
"log4rs",
- "miette",
+ "miette 3.3.0",
"nix",
"once_cell",
"regex",
"rmp-serde",
"serde",
"serde_json",
- "serde_yaml",
"signal-hook 0.3.14",
"strip-ansi-escapes",
"strum",
diff --git a/Cargo.toml b/Cargo.toml
index c0ec182d..f0b49a29 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/Makefile.toml b/Makefile.toml
index f8ade7e6..0906d02f 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]
diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs
index 26afcd94..8f0ff42a 100644
--- a/default-plugins/status-bar/src/second_line.rs
+++ b/default-plugins/status-bar/src/second_line.rs
@@ -161,7 +161,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
(s("Embed pane"), s("Embed"), action_key(&km, &[A::TogglePaneEmbedOrFloating, TO_NORMAL])),
(s("Next"), s("Next"), action_key(&km, &[A::SwitchFocus])),
(s("Select pane"), s("Select"), to_normal_key),
- ]} else if mi.mode == IM::Tab {
+ ]} else if mi.mode == IM::Tab {
// With the default bindings, "Move focus" for tabs is tricky: It binds all the arrow keys
// to moving tabs focus (left/up go left, right/down go right). Since we sort the keys
// above and then dedpulicate based on the actions, we will end up with LeftArrow for
@@ -180,9 +180,9 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
vec![
(s("Move focus"), s("Move"), focus_keys),
- (s("New"), s("New"), action_key(&km, &[A::NewTab(None), TO_NORMAL])),
+ (s("New"), s("New"), action_key(&km, &[A::NewTab(None, None), TO_NORMAL])),
(s("Close"), s("Close"), action_key(&km, &[A::CloseTab, TO_NORMAL])),
- (s("Rename"), s("Rename"),
+ (s("Rename"), s("Rename"),
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
(s("Sync"), s("Sync"), action_key(&km, &[A::ToggleActiveSyncTab, TO_NORMAL])),
(s("Toggle"), s("Toggle"), action_key(&km, &[A::ToggleTab])),
@@ -192,7 +192,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
&[A::Resize(RDir::Left)], &[A::Resize(RDir::Down)],
&[A::Resize(RDir::Up)], &[A::Resize(RDir::Right)]])),
(s("Increase/Decrease size"), s("Increase/Decrease"),
- action_key_group(&km, &[&[A::Resize(RDir::Increase)], &[A::Resize(RDir::Decrease)]])),
+ action_key_group(&km, &[&[A::Resize(RDir::Increase)], &[A::Resize(RDir::Decrease)]])),
(s("Select pane"), s("Select"), to_normal_key),
]} else if mi.mode == IM::Move { vec![
(s("Move"), s("Move"), action_key_group(&km, &[
@@ -242,7 +242,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down)), TO_NORMAL])),
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right)), TO_NORMAL])),
(s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
- (s("New tab"), s("New"), action_key(&km, &[A::NewTab(None), TO_NORMAL])),
+ (s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, None), TO_NORMAL])),
(s("Rename tab"), s("Rename"),
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
(s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
diff --git a/default-plugins/status-bar/src/tip/data/quicknav.rs b/default-plugins/status-bar/src/tip/data/quicknav.rs
index 59b604a7..57d60a2d 100644
--- a/default-plugins/status-bar/src/tip/data/quicknav.rs
+++ b/default-plugins/status-bar/src/tip/data/quicknav.rs
@@ -72,13 +72,18 @@ 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/example/themes/README.md b/example/themes/README.md
index ade4d6cd..8572222a 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 00000000..26f20504
--- /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 b9c8a5af..00000000
--- 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 00000000..53c9d6ab
--- /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 676bc42b..00000000
--- 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 00000000..7bb33e68
--- /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 d672ec3b..00000000
--- 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 00000000..6fd96f9a
--- /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 b2135fdf..00000000
--- 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 00000000..a56a9bfb
--- /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 61851ad2..00000000
--- 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 00000000..ade1874b
--- /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 5f3b3968..00000000
--- 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 00000000..20482f5c
--- /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 c93635e4..00000000
--- 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 00000000..93bac49b
--- /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 45683376..00000000
--- 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 00000000..e69de29b
--- /dev/null
+++ b/example/themes/tokyo-night-dark.kdl
diff --git a/example/themes/tokyo-night-light.kdl b/example/themes/tokyo-night-light.kdl
new file mode 100644
index 00000000..3ac5fb76
--- /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 156943cd..00000000
--- 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 00000000..e727ef72
--- /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 9239990f..00000000
--- 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 00000000..4ea1a810
--- /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 d62fb422..00000000
--- 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 58a464d7..b4e2e1da 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -6,13 +6,18 @@ use crate::sessions::{
session_exists, ActiveSession, SessionNameMatch,
};
use dialoguer::Confirm;
-use miette::Result;
+use miette::{Report, Result};
use std::path::PathBuf;
use std::process;
+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::{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::{
@@ -21,10 +26,7 @@ use zellij_utils::{
setup::{get_default_data_dir, Setup},
};
-#[cfg(feature = "unstable")]
-use miette::IntoDiagnostic;
-#[cfg(feature = "unstable")]
-use zellij_utils::input::actions::ActionsFromYaml;
+use std::{fs::File, io::prelude::*};
pub(crate) use crate::sessions::list_sessions;
@@ -118,69 +120,133 @@ 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,
+ requested_session_name: Option<String>,
+) {
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);
+ 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) = opts.session.clone() {
- attach_with_fake_client(opts, &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_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());
+ eprintln!("Please specify the session name to send actions to. The following sessions are active:");
+ print_sessions(existing_sessions);
std::process::exit(1);
}
},
};
}
+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();
+ let _ = handle.read_to_string(&mut raw_config_file);
+ match config_yaml_to_config_kdl(&raw_config_file, false) {
+ 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);
+ },
+ }
+}
-#[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::<ActionsFromYaml>(&action).into_diagnostic() {
- Ok(parsed) => {
- let (config, _, config_options) = match Setup::from_options(&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);
+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();
+ 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);
+ },
+ }
+}
- 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);
- },
- };
- };
+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();
+ 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) {
+ 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) => {
+ eprintln!("{}", e);
+ log::error!("Error sending action: {}", e);
+ std::process::exit(2);
+ },
+ }
}
fn attach_with_session_index(config_options: Options, index: usize, create: bool) -> ClientInfo {
@@ -249,10 +315,17 @@ fn attach_with_session_name(
}
pub(crate) fn start_client(opts: CliArgs) {
- let (config, layout, config_options) = match Setup::from_options(&opts) {
+ // 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) => {
- eprintln!("{}", e);
+ if let ConfigError::KdlError(error) = e {
+ let report: Report = error.into();
+ eprintln!("{:?}", report);
+ } else {
+ eprintln!("{}", e);
+ }
process::exit(1);
},
};
@@ -285,7 +358,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,23 +387,21 @@ 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() {
+ 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),
+ Some(session_name.clone()),
config_options.clone(),
true,
);
-
let attach_layout = match client {
ClientInfo::Attach(_, _) => None,
- ClientInfo::New(_) => layout,
+ ClientInfo::New(_) => Some(layout),
};
-
start_client_impl(
Box::new(os_input),
opts,
@@ -339,20 +410,23 @@ pub(crate) fn start_client(opts: CliArgs) {
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,
+ config_options.clone(),
+ ClientInfo::New(session_name.clone()),
+ Some(layout),
);
- }
-
- process::exit(0);
+ },
}
+ // 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();
@@ -363,7 +437,7 @@ pub(crate) fn start_client(opts: CliArgs) {
config,
config_options,
ClientInfo::New(session_name),
- layout,
+ Some(layout),
);
}
}
diff --git a/src/main.rs b/src/main.rs
index ca05dbf1..d2363e7b 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::{CliAction, CliArgs, Command, Sessions},
logging::*,
};
@@ -14,10 +14,53 @@ 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);
+ }
+ if let Some(Command::Sessions(Sessions::Command {
+ command,
+ direction,
+ cwd,
+ floating,
+ })) = opts.command
+ {
+ let command_cli_action = CliAction::NewPane {
+ command,
+ direction,
+ cwd,
+ floating,
+ };
+ commands::send_action_to_session(command_cli_action, opts.session);
+ std::process::exit(0);
+ }
+ 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 })) = opts.command {
+ commands::convert_old_layout_file(old_layout_file);
+ std::process::exit(0);
+ }
+ 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/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
index 7457ed54..63e2d677 100644
--- a/src/tests/e2e/cases.rs
+++ b/src/tests/e2e/cases.rs
@@ -458,9 +458,8 @@ 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")
- && remote_terminal.tip_appears()
{
// cursor is in the first tab again
step_is_complete = true;
@@ -475,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]
@@ -950,47 +950,12 @@ pub fn detach_and_attach_session() {
#[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() {
let fake_win_size = Size {
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);
@@ -1720,43 +1685,6 @@ pub fn toggle_floating_panes() {
#[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() {
let fake_win_size = Size {
cols: 120,
diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs
index 03fb619e..b0bde1db 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();
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 8b66d5ac..dcb76b9e 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 0d431c32..ac55a9d4 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 603432fd..b4ad5a98 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ <←→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <TAB> Toggle / <ENTER> Select 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 92d2a037..5f79887f 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 ef923aa2..d373cbbb 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 68965f48..3a421290 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 ffcb6b2f..5bf97567 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 797448a6..2316cf55 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 cd4006d5..d3c93217 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 8856f1f9..86e8023d 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 d7328b8d..161f8eea 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 6bc0369c..15cc156a 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 2d84b3f3..2aa80115 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 f0365630..c4dd1fff 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 de1ef639..d7bf8cd5 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 + <n> / Alt + <←↓↑→> or Alt + <hjkl> / Alt + <+|=|->
+ QuickNav: Alt + <n> / Alt + <←↓↑→> or Alt + <hjkl> / 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 ec416eea..e5132b9f 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 22b79627..8dc5897b 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 f066d207..0fe416cf 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 1f4ff4f0..6f2822c5 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 05985a5f..e812fda8 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 3a1643b1..9fdd0f2d 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 03b44ebb..e8c27717 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 ed9a8e20..56b417d7 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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|=|-> => resize pane.
+ Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => 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 00000000..6123b1d5
--- /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 7d2a8c71..00000000
--- 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/parts-total-less-than-100-percent.yaml b/src/tests/fixtures/layouts/parts-total-less-than-100-percent.yaml
deleted file mode 100644
index aac8c457..00000000
--- 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 8f3fcdbd..00000000
--- 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 2d0aed95..00000000
--- 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-client/Cargo.toml b/zellij-client/Cargo.toml
index 30370d47..5761b0e5 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/cli_client.rs b/zellij-client/src/cli_client.rs
new file mode 100644
index 00000000..95cd9d13
--- /dev/null
+++ b/zellij-client/src/cli_client.rs
@@ -0,0 +1,34 @@
+//! The `[cli_client]` is used to attach to a running server session
+//! and dispatch actions, that are specified through the command line.
+use std::process;
+use std::{fs, path::PathBuf};
+
+use crate::os_input_output::ClientOsApi;
+use zellij_utils::{
+ input::actions::Action,
+ ipc::{ClientToServerMsg, ServerToClientMsg},
+};
+
+pub fn start_cli_client(os_input: Box<dyn ClientOsApi>, session_name: &str, actions: Vec<Action>) {
+ 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, 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-client/src/fake_client.rs b/zellij-client/src/fake_client.rs
deleted file mode 100644
index 475298e9..00000000
--- 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::LayoutFromYaml, options::Options},
- ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg},
-};
-
-pub fn start_fake_client(
- os_input: Box<dyn ClientOsApi>,
- _opts: CliArgs,
- config: Config,
- config_options: Options,
- info: ClientInfo,
- _layout: Option<LayoutFromYaml>,
- actions: Vec<Action>,
-) {
- 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,
- },
- keybinds: config.keybinds.clone(),
- };
-
- 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<ClientId>;
- 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/input_handler.rs b/zellij-client/src/input_handler.rs
index bebf1ee7..076ab35d 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,
},
@@ -133,7 +132,9 @@ impl InputHandler {
}
fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) {
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;
@@ -230,65 +231,6 @@ impl InputHandler {
},
}
}
- fn handle_actions(&mut self, actions: Vec<Action>, session_name: &str, clients: Vec<ClientId>) {
- 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
@@ -329,7 +271,7 @@ impl InputHandler {
| Action::Run(_)
| Action::ToggleFloatingPanes
| Action::TogglePaneEmbedOrFloating
- | Action::NewTab(_)
+ | Action::NewTab(..)
| Action::GoToNextTab
| Action::GoToPreviousTab
| Action::CloseTab
@@ -381,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<dyn ClientOsApi>,
- config: Config,
- options: Options,
- command_is_executing: CommandIsExecuting,
- clients: Vec<ClientId>,
- send_client_instructions: SenderWithContext<ClientInstruction>,
- default_mode: InputMode,
- receive_input_instructions: Receiver<(InputInstruction, ErrorContext)>,
- actions: Vec<Action>,
- 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 9c5169cc..bac08715 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -1,9 +1,9 @@
pub mod os_input_output;
+pub mod cli_client;
mod command_is_executing;
-pub mod fake_client;
mod input_handler;
-mod sessions;
+pub mod old_config_converter;
mod stdin_ansi_parser;
mod stdin_handler;
@@ -30,7 +30,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};
/// Instructions related to the client-side application
#[derive(Debug, Clone)]
@@ -124,7 +124,7 @@ pub fn start_client(
config: Config,
config_options: Options,
info: ClientInfo,
- layout: Option<LayoutFromYaml>,
+ layout: Option<Layout>,
) {
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";
@@ -143,20 +143,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
+ .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,
+ rounded_corners: config.ui.pane_frames.rounded_corners,
},
keybinds: config.keybinds.clone(),
};
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 00000000..0d5bf2e5
--- /dev/null
+++ b/zellij-client/src/old_config_converter/convert_old_yaml_files.rs
@@ -0,0 +1,332 @@
+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},
+};
+
+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<PathBuf>) {
+ 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<PathBuf>) -> 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<PathBuf>,
+ yaml_config_was_explicitly_set: bool,
+) -> Result<bool, String> {
+ 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
new file mode 100644
index 00000000..ac6d7676
--- /dev/null
+++ b/zellij-client/src/old_config_converter/mod.rs
@@ -0,0 +1,6 @@
+mod convert_old_yaml_files;
+mod old_config;
+mod old_layout;
+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;
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 00000000..73e098b7
--- /dev/null
+++ b/zellij-client/src/old_config_converter/old_config.rs
@@ -0,0 +1,1292 @@
+// This is a converter from the old yaml config to the new KDL config.
+//
+// It is supposed to be mostly self containing - please refrain from adding to it, importing
+// from it or changing it
+use std::fmt;
+use std::path::PathBuf;
+
+use serde::de::{Error, Visitor};
+use serde::{Deserialize, Deserializer, Serialize};
+use std::collections::{BTreeMap, HashMap};
+use url::Url;
+
+const ON_FORCE_CLOSE_DESCRIPTION: &'static str = "
+// Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP
+// eg. when terminal window with an active zellij session is closed
+// Options:
+// - detach (Default)
+// - quit
+//
+";
+
+const SIMPLIFIED_UI_DESCRIPTION: &'static str = "
+// Send a request for a simplified ui (without arrow fonts) to plugins
+// Options:
+// - true
+// - false (Default)
+//
+";
+
+const DEFAULT_SHELL_DESCRIPTION: &'static str = "
+// Choose the path to the default shell that zellij will use for opening new panes
+// Default: $SHELL
+//
+";
+
+const PANE_FRAMES_DESCRIPTION: &'static str = "
+// Toggle between having pane frames around the panes
+// Options:
+// - true (default)
+// - false
+//
+";
+
+const DEFAULT_THEME_DESCRIPTION: &'static str = "
+// Choose the theme that is specified in the themes section.
+// Default: default
+//
+";
+
+const DEFAULT_MODE_DESCRIPTION: &'static str = "
+// Choose the mode that zellij uses when starting up.
+// Default: normal
+//
+";
+
+const MOUSE_MODE_DESCRIPTION: &'static str = "
+// Toggle enabling the mouse mode.
+// On certain configurations, or terminals this could
+// potentially interfere with copying text.
+// Options:
+// - true (default)
+// - false
+//
+";
+
+const SCROLL_BUFFER_SIZE_DESCRIPTION: &'static str = "
+// Configure the scroll back buffer size
+// This is the number of lines zellij stores for each pane in the scroll back
+// buffer. Excess number of lines are discarded in a FIFO fashion.
+// Valid values: positive integers
+// Default value: 10000
+//
+";
+
+const COPY_COMMAND_DESCRIPTION: &'static str = "
+// Provide a command to execute when copying text. The text will be piped to
+// the stdin of the program to perform the copy. This can be used with
+// terminal emulators which do not support the OSC 52 ANSI control sequence
+// that will be used by default if this option is not set.
+// Examples:
+//
+// copy_command \"xclip -selection clipboard\" // x11
+// copy_command \"wl-copy\" // wayland
+// copy_command \"pbcopy\" // osx
+";
+
+const COPY_CLIPBOARD_DESCRIPTION: &'static str = "
+// Choose the destination for copied text
+// Allows using the primary selection buffer (on x11/wayland) instead of the system clipboard.
+// Does not apply when using copy_command.
+// Options:
+// - system (default)
+// - primary
+//
+";
+
+const COPY_ON_SELECT_DESCRIPTION: &'static str = "
+// Enable or disable automatic copy (and clear) of selection when releasing mouse
+// Default: true
+//
+";
+
+const SCROLLBACK_EDITOR_DESCRIPTION: &'static str = "
+// Path to the default editor to use to edit pane scrollbuffer
+// Default: $EDITOR or $VISUAL
+//
+";
+
+const MIRROR_SESSION_DESCRIPTION: &'static str = "
+// When attaching to an existing session with other users,
+// should the session be mirrored (true)
+// or should each user have their own cursor (false)
+// Default: false
+//
+";
+
+const DEFAULT_LAYOUT_DESCRIPTION: &'static str = "
+// The name of the default layout to load on startup
+// Default: \"default\"
+//
+";
+
+const LAYOUT_DIR_DESCRIPTION: &'static str = "
+// The folder in which Zellij will look for layouts
+//
+";
+
+const THEME_DIR_DESCRIPTION: &'static str = "
+// The folder in which Zellij will look for themes
+//
+";
+
+fn options_yaml_to_options_kdl(options_yaml: &OldOptions, no_comments: bool) -> String {
+ let mut options_kdl = String::new();
+
+ macro_rules! push_option {
+ ($attribute_name:ident, $description_text:ident, $present_pattern:expr) => {
+ if !no_comments {
+ options_kdl.push_str($description_text);
+ }
+ if let Some($attribute_name) = &options_yaml.$attribute_name {
+ options_kdl.push_str(&format!($present_pattern, $attribute_name));
+ options_kdl.push('\n');
+ };
+ };
+ ($attribute_name:ident, $description_text:ident, $present_pattern:expr, $absent_pattern:expr) => {
+ if !no_comments {
+ options_kdl.push_str($description_text);
+ }
+ match &options_yaml.$attribute_name {
+ Some($attribute_name) => {
+ options_kdl.push_str(&format!($present_pattern, $attribute_name));
+ },
+ None => {
+ if !no_comments {
+ options_kdl.push_str(&format!($absent_pattern));
+ }
+ },
+ };
+ if !no_comments || options_yaml.$attribute_name.is_some() {
+ options_kdl.push('\n');
+ }
+ };
+ }
+
+ push_option!(
+ on_force_close,
+ ON_FORCE_CLOSE_DESCRIPTION,
+ "on_force_close \"{}\"",
+ "// on_force_close \"quit\""
+ );
+ push_option!(
+ simplified_ui,
+ SIMPLIFIED_UI_DESCRIPTION,
+ "simplified_ui {}",
+ "// simplified_ui true"
+ );
+ push_option!(
+ default_shell,
+ DEFAULT_SHELL_DESCRIPTION,
+ "default_shell {:?}",
+ "// default_shell \"fish\""
+ );
+ push_option!(
+ pane_frames,
+ PANE_FRAMES_DESCRIPTION,
+ "pane_frames {}",
+ "// pane_frames true"
+ );
+ push_option!(
+ theme,
+ DEFAULT_THEME_DESCRIPTION,
+ "theme {:?} ",
+ "// theme \"default\""
+ );
+ push_option!(
+ default_layout,
+ DEFAULT_LAYOUT_DESCRIPTION,
+ "default_layout {:?}",
+ "// default_layout \"compact\""
+ );
+ push_option!(
+ default_mode,
+ DEFAULT_MODE_DESCRIPTION,
+ "default_mode \"{}\"",
+ "// default_mode \"locked\""
+ );
+ push_option!(
+ mouse_mode,
+ MOUSE_MODE_DESCRIPTION,
+ "mouse_mode {}",
+ "// mouse_mode false"
+ );
+ push_option!(
+ scroll_buffer_size,
+ SCROLL_BUFFER_SIZE_DESCRIPTION,
+ "scroll_buffer_size {}",
+ "// scroll_buffer_size 10000"
+ );
+ push_option!(copy_command, COPY_COMMAND_DESCRIPTION, "copy_command {:?}");
+ push_option!(
+ copy_clipboard,
+ COPY_CLIPBOARD_DESCRIPTION,
+ "copy_clipboard \"{}\"",
+ "// copy_clipboard \"primary\""
+ );
+ push_option!(
+ copy_on_select,
+ COPY_ON_SELECT_DESCRIPTION,
+ "copy_on_select {}",
+ "// copy_on_select false"
+ );
+ push_option!(
+ scrollback_editor,
+ SCROLLBACK_EDITOR_DESCRIPTION,
+ "scrollback_editor {:?}",
+ "// scrollback_editor \"/usr/bin/vim\""
+ );
+ push_option!(
+ mirror_session,
+ MIRROR_SESSION_DESCRIPTION,
+ "mirror_session {}",
+ "// mirror_session true"
+ );
+ push_option!(
+ layout_dir,
+ LAYOUT_DIR_DESCRIPTION,
+ "layout_dir {:?}",
+ "// layout_dir /path/to/my/layout_dir"
+ );
+ push_option!(
+ theme_dir,
+ THEME_DIR_DESCRIPTION,
+ "theme_dir {:?}",
+ "// theme_dir \"/path/to/my/theme_dir\""
+ );
+
+ options_kdl
+}
+
+fn env_yaml_to_env_kdl(env_yaml: &OldEnvironmentVariablesFromYaml) -> String {
+ let mut env_kdl = String::new();
+ let mut env_vars: Vec<(String, String)> = env_yaml
+ .env
+ .iter()
+ .map(|(name, val)| (name.clone(), val.clone()))
+ .collect();
+ env_vars.sort_unstable();
+ env_kdl.push_str("env {\n");
+ for (name, val) in env_vars {
+ env_kdl.push_str(&format!(" {} \"{}\"\n", name, val));
+ }
+ env_kdl.push_str("}\n");
+ env_kdl
+}
+
+fn plugins_yaml_to_plugins_kdl(plugins_yaml_to_plugins_kdl: &OldPluginsConfigFromYaml) -> String {
+ let mut plugins_kdl = String::new();
+ if !&plugins_yaml_to_plugins_kdl.0.is_empty() {
+ plugins_kdl.push_str("\n");
+ plugins_kdl.push_str("plugins {\n")
+ }
+ for plugin_config in &plugins_yaml_to_plugins_kdl.0 {
+ if plugin_config._allow_exec_host_cmd {
+ plugins_kdl.push_str(&format!(
+ " {} {{ path {:?}; _allow_exec_host_cmd true; }}\n",
+ plugin_config.tag.0, plugin_config.path
+ ));
+ } else {
+ plugins_kdl.push_str(&format!(
+ " {} {{ path {:?}; }}\n",
+ plugin_config.tag.0, plugin_config.path
+ ));
+ }
+ }
+ if !&plugins_yaml_to_plugins_kdl.0.is_empty() {
+ plugins_kdl.push_str("}\n")
+ }
+ plugins_kdl
+}
+
+fn ui_config_yaml_to_ui_config_kdl(ui_config_yaml: &OldUiConfigFromYaml) -> String {
+ let mut kdl_ui_config = String::new();
+ if ui_config_yaml.pane_frames.rounded_corners {
+ kdl_ui_config.push_str("\n");
+ kdl_ui_config.push_str("ui {\n");
+ kdl_ui_config.push_str(" pane_frames {\n");
+ kdl_ui_config.push_str(" rounded_corners true\n");
+ kdl_ui_config.push_str(" }\n");
+ kdl_ui_config.push_str("}\n");
+ } else {
+ // I'm not sure this is a thing, but since it's possible, why not?
+ kdl_ui_config.push_str("\n");
+ kdl_ui_config.push_str("ui {\n");
+ kdl_ui_config.push_str(" pane_frames {\n");
+ kdl_ui_config.push_str(" rounded_corners false\n");
+ kdl_ui_config.push_str(" }\n");
+ kdl_ui_config.push_str("}\n");
+ }
+ kdl_ui_config
+}
+
+fn theme_config_yaml_to_theme_config_kdl(
+ theme_config_yaml: &OldThemesFromYamlIntermediate,
+) -> String {
+ macro_rules! theme_color {
+ ($theme:ident, $color:ident, $color_name:expr, $kdl_theme_config:expr) => {
+ match $theme.palette.$color {
+ OldPaletteColorFromYaml::Rgb((r, g, b)) => {
+ $kdl_theme_config
+ .push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b));
+ },
+ OldPaletteColorFromYaml::EightBit(eight_bit_color) => {
+ $kdl_theme_config
+ .push_str(&format!(" {} {}\n", $color_name, eight_bit_color));
+ },
+ OldPaletteColorFromYaml::Hex(OldHexColor(r, g, b)) => {
+ $kdl_theme_config
+ .push_str(&format!(" {} {} {} {}\n", $color_name, r, g, b));
+ },
+ }
+ };
+ }
+
+ let mut kdl_theme_config = String::new();
+ if !theme_config_yaml.0.is_empty() {
+ kdl_theme_config.push_str("themes {\n")
+ }
+ let mut themes: Vec<(String, OldTheme)> = theme_config_yaml
+ .0
+ .iter()
+ .map(|(theme_name, theme)| (theme_name.clone(), theme.clone()))
+ .collect();
+ themes.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
+ for (theme_name, theme) in themes {
+ kdl_theme_config.push_str(&format!(" {} {{\n", theme_name));
+ theme_color!(theme, fg, "fg", kdl_theme_config);
+ theme_color!(theme, bg, "bg", kdl_theme_config);
+ theme_color!(theme, black, "black", kdl_theme_config);
+ theme_color!(theme, red, "red", kdl_theme_config);
+ theme_color!(theme, green, "green", kdl_theme_config);
+ theme_color!(theme, yellow, "yellow", kdl_theme_config);
+ theme_color!(theme, blue, "blue", kdl_theme_config);
+ theme_color!(theme, magenta, "magenta", kdl_theme_config);
+ theme_color!(theme, cyan, "cyan", kdl_theme_config);
+ theme_color!(theme, white, "white", kdl_theme_config);
+ theme_color!(theme, orange, "orange", kdl_theme_config);
+ kdl_theme_config.push_str(" }\n");
+ }
+ if !theme_config_yaml.0.is_empty() {
+ kdl_theme_config.push_str("}\n")
+ }
+ kdl_theme_config
+}
+
+fn keybinds_yaml_to_keybinds_kdl(keybinds_yaml: &OldKeybindsFromYaml) -> String {
+ let mut kdl_keybinds = String::new();
+ let modes = vec![
+ // mode sort order
+ OldInputMode::Normal,
+ OldInputMode::Locked,
+ OldInputMode::Pane,
+ OldInputMode::Tab,
+ OldInputMode::Resize,
+ OldInputMode::Move,
+ OldInputMode::Scroll,
+ OldInputMode::Session,
+ OldInputMode::Search,
+ OldInputMode::EnterSearch,
+ OldInputMode::RenameTab,
+ OldInputMode::RenamePane,
+ OldInputMode::Prompt,
+ OldInputMode::Tmux,
+ ];
+
+ // title and global unbinds / clear-defaults
+ match &keybinds_yaml.unbind {
+ OldUnbind::Keys(keys_to_unbind) => {
+ kdl_keybinds.push_str("keybinds {\n");
+ let key_string: String = keys_to_unbind
+ .iter()
+ .map(|k| format!("\"{}\"", k))
+ .collect::<Vec<String>>()
+ .join(" ");
+ kdl_keybinds.push_str(&format!(" unbind {}\n", key_string));
+ },
+ OldUnbind::All(should_unbind_all_defaults) => {
+ if *should_unbind_all_defaults {
+ kdl_keybinds.push_str("keybinds clear-defaults=true {\n");
+ } else {
+ kdl_keybinds.push_str("keybinds {\n");
+ }
+ },
+ }
+
+ for mode in modes {
+ if let Some(mode_keybinds) = keybinds_yaml.keybinds.get(&mode) {
+ let mut should_clear_mode_defaults = false;
+ let mut kdl_mode_keybinds = String::new();
+ for key_action_unbind in mode_keybinds {
+ match key_action_unbind {
+ OldKeyActionUnbind::KeyAction(key_action) => {
+ let keys = &key_action.key;
+ let actions = &key_action.action;
+ let key_string: String = keys
+ .iter()
+ .map(|k| format!("\"{}\"", k))
+ .collect::<Vec<String>>()
+ .join(" ");
+ let actions_string: String = actions
+ .iter()
+ .map(|a| format!("{};", a))
+ .collect::<Vec<String>>()
+ .join(" ");
+ kdl_mode_keybinds.push_str(&format!(
+ " bind {} {{ {} }}\n",
+ key_string, actions_string
+ ));
+ },
+ OldKeyActionUnbind::Unbind(unbind) => match &unbind.unbind {
+ OldUnbind::Keys(keys_to_unbind) => {
+ let key_string: String = keys_to_unbind
+ .iter()
+ .map(|k| format!("\"{}\"", k))
+ .collect::<Vec<String>>()
+ .join(" ");
+ kdl_mode_keybinds.push_str(&format!(" unbind {}\n", key_string));
+ },
+ OldUnbind::All(unbind_all) => {
+ if *unbind_all {
+ should_clear_mode_defaults = true;
+ }
+ },
+ },
+ }
+ }
+ if should_clear_mode_defaults {
+ kdl_keybinds.push_str(&format!(" {} clear-defaults=true {{\n", mode));
+ } else {
+ kdl_keybinds.push_str(&format!(" {} {{\n", mode));
+ }
+ kdl_keybinds.push_str(&kdl_mode_keybinds);
+ kdl_keybinds.push_str(" }\n");
+ }
+ }
+ kdl_keybinds.push_str("}\n");
+ kdl_keybinds
+}
+
+pub fn config_yaml_to_config_kdl(
+ raw_yaml_config: &str,
+ no_comments: bool,
+) -> Result<String, String> {
+ // returns the raw kdl config
+ let config_from_yaml: OldConfigFromYaml = serde_yaml::from_str(raw_yaml_config)
+ .map_err(|e| format!("Failed to parse yaml: {:?}", e))?;
+ let mut kdl_config = String::new();
+ if let Some(old_config_keybinds) = config_from_yaml.keybinds.as_ref() {
+ kdl_config.push_str(&keybinds_yaml_to_keybinds_kdl(old_config_keybinds));
+ }
+ if let Some(old_config_options) = config_from_yaml.options.as_ref() {
+ kdl_config.push_str(&options_yaml_to_options_kdl(
+ old_config_options,
+ no_comments,
+ ));
+ }
+ if let Some(old_config_env_variables) = config_from_yaml.env.as_ref() {
+ kdl_config.push_str(&env_yaml_to_env_kdl(old_config_env_variables));
+ }
+ kdl_config.push_str(&plugins_yaml_to_plugins_kdl(&config_from_yaml.plugins));
+ if let Some(old_ui_config) = config_from_yaml.ui.as_ref() {
+ kdl_config.push_str(&ui_config_yaml_to_ui_config_kdl(old_ui_config));
+ }
+ if let Some(old_themes_config) = config_from_yaml.themes.as_ref() {
+ kdl_config.push_str(&theme_config_yaml_to_theme_config_kdl(old_themes_config));
+ }
+ Ok(kdl_config)
+}
+
+#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq)]
+pub struct OldConfigFromYaml {
+ #[serde(flatten)]
+ pub options: Option<OldOptions>,
+ pub keybinds: Option<OldKeybindsFromYaml>,
+ pub themes: Option<OldThemesFromYamlIntermediate>,
+ #[serde(flatten)]
+ pub env: Option<OldEnvironmentVariablesFromYaml>,
+ #[serde(default)]
+ pub plugins: OldPluginsConfigFromYaml,
+ pub ui: Option<OldUiConfigFromYaml>,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct OldKeybindsFromYaml {
+ #[serde(flatten)]
+ keybinds: HashMap<OldInputMode, Vec<OldKeyActionUnbind>>,
+ #[serde(default)]
+ unbind: OldUnbind,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
+#[serde(untagged)]
+enum OldUnbind {
+ // This is the correct order, don't rearrange!
+ // Suspected Bug in the untagged macro.
+ // 1. Keys
+ Keys(Vec<OldKey>),
+ // 2. All
+ All(bool),
+}
+
+impl Default for OldUnbind {
+ fn default() -> OldUnbind {
+ OldUnbind::All(false)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+#[serde(untagged)]
+enum OldKeyActionUnbind {
+ KeyAction(OldKeyActionFromYaml),
+ Unbind(OldUnbindFromYaml),
+}
+
+/// Intermediate struct used for deserialisation
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+struct OldKeyActionFromYaml {
+ action: Vec<OldAction>,
+ key: Vec<OldKey>,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+struct OldUnbindFromYaml {
+ unbind: OldUnbind,
+}
+
+/// Main configuration.
+#[derive(Debug, Clone, PartialEq, Deserialize)]
+struct OldConfig {
+ pub keybinds: OldKeybinds,
+ pub options: OldOptions,
+ pub themes: Option<OldThemesFromYamlIntermediate>,
+ pub plugins: OldPluginsConfig,
+ pub ui: Option<OldUiConfigFromYaml>,
+ pub env: OldEnvironmentVariablesFromYaml,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+struct OldKeybinds(HashMap<OldInputMode, OldModeKeybinds>);
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+struct OldModeKeybinds(BTreeMap<OldKey, Vec<OldAction>>);
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+pub struct OldThemesFromYamlIntermediate(HashMap<String, OldTheme>);
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
+struct OldPaletteFromYaml {
+ pub fg: OldPaletteColorFromYaml,
+ pub bg: OldPaletteColorFromYaml,
+ pub black: OldPaletteColorFromYaml,
+ pub red: OldPaletteColorFromYaml,
+ pub green: OldPaletteColorFromYaml,
+ pub yellow: OldPaletteColorFromYaml,
+ pub blue: OldPaletteColorFromYaml,
+ pub magenta: OldPaletteColorFromYaml,
+ pub cyan: OldPaletteColorFromYaml,
+ pub white: OldPaletteColorFromYaml,
+ pub orange: OldPaletteColorFromYaml,
+}
+
+/// Intermediate deserialization enum
+// This is here in order to make the untagged enum work
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+#[serde(untagged)]
+enum OldPaletteColorFromYaml {
+ Rgb((u8, u8, u8)),
+ EightBit(u8),
+ Hex(OldHexColor),
+}
+
+impl From<OldHexColor> for (u8, u8, u8) {
+ fn from(e: OldHexColor) -> (u8, u8, u8) {
+ let OldHexColor(r, g, b) = e;
+ (r, g, b)
+ }
+}
+
+struct OldHexColorVisitor();
+
+impl<'de> Visitor<'de> for OldHexColorVisitor {
+ type Value = OldHexColor;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ write!(formatter, "a hex color in the format #RGB or #RRGGBB")
+ }
+
+ fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ if let Some(stripped) = s.strip_prefix('#') {
+ return self.visit_str(stripped);
+ }
+
+ if s.len() == 3 {
+ Ok(OldHexColor(
+ u8::from_str_radix(&s[0..1], 16).map_err(E::custom)? * 0x11,
+ u8::from_str_radix(&s[1..2], 16).map_err(E::custom)? * 0x11,
+ u8::from_str_radix(&s[2..3], 16).map_err(E::custom)? * 0x11,
+ ))
+ } else if s.len() == 6 {
+ Ok(OldHexColor(
+ u8::from_str_radix(&s[0..2], 16).map_err(E::custom)?,
+ u8::from_str_radix(&s[2..4], 16).map_err(E::custom)?,
+ u8::from_str_radix(&s[4..6], 16).map_err(E::custom)?,
+ ))
+ } else {
+ Err(Error::custom(
+ "Hex color must be of form \"#RGB\" or \"#RRGGBB\"",
+ ))
+ }
+ }
+}
+
+impl<'de> Deserialize<'de> for OldHexColor {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_str(OldHexColorVisitor())
+ }
+}
+
+impl Default for OldPaletteColorFromYaml {
+ fn default() -> Self {
+ OldPaletteColorFromYaml::EightBit(0)
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
+struct OldHexColor(u8, u8, u8);
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+struct OldTheme {
+ #[serde(flatten)]
+ palette: OldPaletteFromYaml,
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
+pub struct OldUiConfigFromYaml {
+ pub pane_frames: OldFrameConfigFromYaml,
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
+pub struct OldFrameConfigFromYaml {
+ pub rounded_corners: bool,
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
+pub struct OldEnvironmentVariablesFromYaml {
+ env: HashMap<String, String>,
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
+pub struct OldPluginsConfigFromYaml(Vec<OldPluginConfigFromYaml>);
+
+#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
+struct OldPluginConfigFromYaml {
+ pub path: PathBuf,
+ pub tag: OldPluginTag,
+ #[serde(default)]
+ pub run: OldPluginTypeFromYaml,
+ #[serde(default)]
+ pub config: serde_yaml::Value,
+ #[serde(default)]
+ pub _allow_exec_host_cmd: bool,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+enum OldPluginTypeFromYaml {
+ Headless,
+ Pane,
+}
+
+impl Default for OldPluginTypeFromYaml {
+ fn default() -> Self {
+ Self::Pane
+ }
+}
+
+/// Tag used to identify the plugin in layout and config yaml files
+#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
+struct OldPluginTag(String);
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+struct OldPluginsConfig(HashMap<OldPluginTag, OldPluginConfig>);
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+struct OldPluginConfig {
+ /// Path of the plugin, see resolve_wasm_bytes for resolution semantics
+ pub path: PathBuf,
+ /// Plugin type
+ pub run: OldPluginType,
+ /// Allow command execution from plugin
+ pub _allow_exec_host_cmd: bool,
+ /// Original location of the
+ pub location: OldRunPluginLocation,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+enum OldRunPluginLocation {
+ File(PathBuf),
+ Zellij(OldPluginTag),
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+enum OldPluginType {
+ /// Starts immediately when Zellij is started and runs without a visible pane
+ Headless,
+ /// Runs once per pane declared inside a layout file
+ Pane(Option<usize>), // tab_index
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub enum OldOnForceClose {
+ #[serde(alias = "quit")]
+ Quit,
+ #[serde(alias = "detach")]
+ Detach,
+}
+
+impl std::fmt::Display for OldOnForceClose {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::Quit => write!(f, "quit"),
+ Self::Detach => write!(f, "detach"),
+ }
+ }
+}
+
+impl Default for OldOnForceClose {
+ fn default() -> Self {
+ Self::Detach
+ }
+}
+
+#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
+pub enum OldClipboard {
+ #[serde(alias = "system")]
+ System,
+ #[serde(alias = "primary")]
+ Primary,
+}
+
+impl std::fmt::Display for OldClipboard {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::System => write!(f, "system"),
+ Self::Primary => write!(f, "primary"),
+ }
+ }
+}
+
+impl Default for OldClipboard {
+ fn default() -> Self {
+ Self::System
+ }
+}
+
+#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize)]
+pub struct OldOptions {
+ #[serde(default)]
+ pub simplified_ui: Option<bool>,
+ pub theme: Option<String>,
+ pub default_mode: Option<OldInputMode>,
+ pub default_shell: Option<PathBuf>,
+ pub default_layout: Option<PathBuf>,
+ pub layout_dir: Option<PathBuf>,
+ pub theme_dir: Option<PathBuf>,
+ #[serde(default)]
+ pub mouse_mode: Option<bool>,
+ #[serde(default)]
+ pub pane_frames: Option<bool>,
+ #[serde(default)]
+ pub mirror_session: Option<bool>,
+ pub on_force_close: Option<OldOnForceClose>,
+ pub scroll_buffer_size: Option<usize>,
+ #[serde(default)]
+ pub copy_command: Option<String>,
+ #[serde(default)]
+ pub copy_clipboard: Option<OldClipboard>,
+ #[serde(default)]
+ pub copy_on_select: Option<bool>,
+ pub scrollback_editor: Option<PathBuf>,
+}
+
+/// Describes the different input modes, which change the way that keystrokes will be interpreted.
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
+pub enum OldInputMode {
+ /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading
+ /// to other modes
+ #[serde(alias = "normal")]
+ Normal,
+ /// In `Locked` mode, input is always written to the terminal and all shortcuts are disabled
+ /// except the one leading back to normal mode
+ #[serde(alias = "locked")]
+ Locked,
+ /// `Resize` mode allows resizing the different existing panes.
+ #[serde(alias = "resize")]
+ Resize,
+ /// `Pane` mode allows creating and closing panes, as well as moving between them.
+ #[serde(alias = "pane")]
+ Pane,
+ /// `Tab` mode allows creating and closing tabs, as well as moving between them.
+ #[serde(alias = "tab")]
+ Tab,
+ /// `Scroll` mode allows scrolling up and down within a pane.
+ #[serde(alias = "scroll")]
+ Scroll,
+ /// `EnterSearch` mode allows for typing in the needle for a search in the scroll buffer of a pane.
+ #[serde(alias = "entersearch")]
+ EnterSearch,
+ /// `Search` mode allows for searching a term in a pane (superset of `Scroll`).
+ #[serde(alias = "search")]
+ Search,
+ /// `RenameTab` mode allows assigning a new name to a tab.
+ #[serde(alias = "renametab")]
+ RenameTab,
+ /// `RenamePane` mode allows assigning a new name to a pane.
+ #[serde(alias = "renamepane")]
+ RenamePane,
+ /// `Session` mode allows detaching sessions
+ #[serde(alias = "session")]
+ Session,
+ /// `Move` mode allows moving the different existing panes within a tab
+ #[serde(alias = "move")]
+ Move,
+ /// `Prompt` mode allows interacting with active prompts.
+ #[serde(alias = "prompt")]
+ Prompt,
+ /// `Tmux` mode allows for basic tmux keybindings functionality
+ #[serde(alias = "tmux")]
+ Tmux,
+}
+
+impl std::fmt::Display for OldInputMode {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::Normal => write!(f, "normal"),
+ Self::Locked => write!(f, "locked"),
+ Self::Resize => write!(f, "resize"),
+ Self::Pane => write!(f, "pane"),
+ Self::Tab => write!(f, "tab"),
+ Self::Scroll => write!(f, "scroll"),
+ Self::EnterSearch => write!(f, "entersearch"),
+ Self::Search => write!(f, "search"),
+ Self::RenameTab => write!(f, "RenameTab"),
+ Self::RenamePane => write!(f, "RenamePane"),
+ Self::Session => write!(f, "session"),
+ Self::Move => write!(f, "move"),
+ Self::Prompt => write!(f, "prompt"),
+ Self::Tmux => write!(f, "tmux"),
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
+enum OldKey {
+ PageDown,
+ PageUp,
+ Left,
+ Down,
+ Up,
+ Right,
+ Home,
+ End,
+ Backspace,
+ Delete,
+ Insert,
+ F(u8),
+ Char(char),
+ Alt(OldCharOrArrow),
+ Ctrl(char),
+ BackTab,
+ Null,
+ Esc,
+}
+
+impl std::fmt::Display for OldKey {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::PageDown => write!(f, "PageDown"),
+ Self::PageUp => write!(f, "PageUp"),
+ Self::Left => write!(f, "Left"),
+ Self::Down => write!(f, "Down"),
+ Self::Up => write!(f, "Up"),
+ Self::Right => write!(f, "Right"),
+ Self::Home => write!(f, "Home"),
+ Self::End => write!(f, "End"),
+ Self::Backspace => write!(f, "Backspace"),
+ Self::Delete => write!(f, "Delete"),
+ Self::Insert => write!(f, "Insert"),
+ Self::F(index) => write!(f, "F{}", index),
+ Self::Char(c) => match c {
+ '\n' => write!(f, "Enter"),
+ '\t' => write!(f, "Tab"),
+ '\"' => write!(f, "\\\""), // make sure it is escaped because otherwise it will be
+ // seen as a KDL string starter/terminator
+ ' ' => write!(f, "Space"),
+ _ => write!(f, "{}", c),
+ },
+ Self::Alt(char_or_arrow) => match char_or_arrow {
+ OldCharOrArrow::Char(c) => write!(f, "Alt {}", c),
+ OldCharOrArrow::Direction(direction) => match direction {
+ OldDirection::Left => write!(f, "Alt Left"),
+ OldDirection::Right => write!(f, "Alt Right"),
+ OldDirection::Up => write!(f, "Alt Up"),
+ OldDirection::Down => write!(f, "Alt Down"),
+ },
+ },
+ Self::Ctrl(c) => write!(f, "Ctrl {}", c),
+ Self::BackTab => write!(f, "Tab"),
+ Self::Null => write!(f, "Null"),
+ Self::Esc => write!(f, "Esc"),
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
+#[serde(untagged)]
+enum OldCharOrArrow {
+ Char(char),
+ Direction(OldDirection),
+}
+
+/// The four directions (left, right, up, down).
+#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
+enum OldDirection {
+ Left,
+ Right,
+ Up,
+ Down,
+}
+
+impl std::fmt::Display for OldDirection {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::Left => write!(f, "Left"),
+ Self::Right => write!(f, "Right"),
+ Self::Up => write!(f, "Up"),
+ Self::Down => write!(f, "Down"),
+ }
+ }
+}
+
+impl Default for OldDirection {
+ fn default() -> Self {
+ OldDirection::Left
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
+enum OldAction {
+ Quit,
+ Write(Vec<u8>),
+ WriteChars(String),
+ SwitchToMode(OldInputMode),
+ Resize(OldResizeDirection),
+ FocusNextPane,
+ FocusPreviousPane,
+ SwitchFocus,
+ MoveFocus(OldDirection),
+ MoveFocusOrTab(OldDirection),
+ MovePane(Option<OldDirection>),
+ DumpScreen(String),
+ EditScrollback,
+ ScrollUp,
+ ScrollUpAt(OldPosition),
+ ScrollDown,
+ ScrollDownAt(OldPosition),
+ ScrollToBottom,
+ PageScrollUp,
+ PageScrollDown,
+ HalfPageScrollUp,
+ HalfPageScrollDown,
+ ToggleFocusFullscreen,
+ TogglePaneFrames,
+ ToggleActiveSyncTab,
+ NewPane(Option<OldDirection>),
+ TogglePaneEmbedOrFloating,
+ ToggleFloatingPanes,
+ CloseFocus,
+ PaneNameInput(Vec<u8>),
+ UndoRenamePane,
+ NewTab(Option<OldTabLayout>),
+ NoOp,
+ GoToNextTab,
+ GoToPreviousTab,
+ CloseTab,
+ GoToTab(u32),
+ ToggleTab,
+ TabNameInput(Vec<u8>),
+ UndoRenameTab,
+ Run(OldRunCommandAction),
+ Detach,
+ LeftClick(OldPosition),
+ RightClick(OldPosition),
+ MiddleClick(OldPosition),
+ LeftMouseRelease(OldPosition),
+ RightMouseRelease(OldPosition),
+ MiddleMouseRelease(OldPosition),
+ MouseHoldLeft(OldPosition),
+ MouseHoldRight(OldPosition),
+ MouseHoldMiddle(OldPosition),
+ Copy,
+ Confirm,
+ Deny,
+ SkipConfirm(Box<OldAction>),
+ SearchInput(Vec<u8>),
+ Search(OldSearchDirection),
+ SearchToggleOption(OldSearchOption),
+}
+
+impl std::fmt::Display for OldAction {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::Quit => write!(f, "Quit"),
+ Self::Write(bytes) => write!(
+ f,
+ "Write {}",
+ bytes
+ .iter()
+ .map(|c| format!("{}", *c))
+ .collect::<Vec<String>>()
+ .join(" ")
+ ),
+ Self::WriteChars(chars) => write!(f, "WriteChars \"{}\"", chars),
+ Self::SwitchToMode(input_mode) => write!(f, "SwitchToMode \"{}\"", input_mode),
+ Self::Resize(resize_direction) => write!(f, "Resize \"{}\"", resize_direction),
+ Self::FocusNextPane => write!(f, "FocusNextPane"),
+ Self::FocusPreviousPane => write!(f, "FocusPreviousPane"),
+ Self::SwitchFocus => write!(f, "SwitchFocus"),
+ Self::MoveFocus(direction) => write!(f, "MoveFocus \"{}\"", direction),
+ Self::MoveFocusOrTab(direction) => write!(f, "MoveFocusOrTab \"{}\"", direction),
+ Self::MovePane(direction) => match direction {
+ Some(direction) => write!(f, "MovePane \"{}\"", direction),
+ None => write!(f, "MovePane"),
+ },
+ Self::DumpScreen(file) => write!(f, "DumpScreen \"{}\"", file),
+ Self::EditScrollback => write!(f, "EditScrollback"),
+ Self::ScrollUp => write!(f, "ScrollUp"),
+ Self::ScrollDown => write!(f, "ScrollDown"),
+ Self::ScrollToBottom => write!(f, "ScrollToBottom"),
+ Self::PageScrollUp => write!(f, "PageScrollUp"),
+ Self::PageScrollDown => write!(f, "PageScrollDown"),
+ Self::HalfPageScrollUp => write!(f, "HalfPageScrollUp"),
+ Self::HalfPageScrollDown => write!(f, "HalfPageScrollDown"),
+ Self::ToggleFocusFullscreen => write!(f, "ToggleFocusFullscreen"),
+ Self::TogglePaneFrames => write!(f, "TogglePaneFrames"),
+ Self::ToggleActiveSyncTab => write!(f, "ToggleActiveSyncTab"),
+ Self::NewPane(direction) => match direction {
+ Some(direction) => write!(f, "NewPane \"{}\"", direction),
+ None => write!(f, "NewPane"),
+ },
+ Self::TogglePaneEmbedOrFloating => write!(f, "TogglePaneEmbedOrFloating"),
+ Self::ToggleFloatingPanes => write!(f, "ToggleFloatingPanes"),
+ Self::CloseFocus => write!(f, "CloseFocus"),
+ Self::PaneNameInput(bytes) => write!(
+ f,
+ "PaneNameInput {}",
+ bytes
+ .iter()
+ .map(|c| format!("{}", *c))
+ .collect::<Vec<String>>()
+ .join(" ")
+ ),
+ Self::UndoRenamePane => write!(f, "UndoRenamePane"),
+ Self::NewTab(_) => write!(f, "NewTab"),
+ Self::NoOp => write!(f, "NoOp"),
+ Self::GoToNextTab => write!(f, "GoToNextTab"),
+ Self::GoToPreviousTab => write!(f, "GoToPreviousTab"),
+ Self::CloseTab => write!(f, "CloseTab"),
+ Self::GoToTab(index) => write!(f, "GoToTab {}", index),
+ Self::ToggleTab => write!(f, "ToggleTab"),
+ // Self::TabNameInput(bytes) => write!(f, "TabNameInput {}", format!("{}", bytes.iter().map(|c| format!("{}", *c)).collect::<Vec<String>>().join(" "))),
+ Self::TabNameInput(bytes) => write!(
+ f,
+ "TabNameInput {}",
+ bytes
+ .iter()
+ .map(|c| format!("{}", *c))
+ .collect::<Vec<String>>()
+ .join(" ")
+ ),
+ Self::UndoRenameTab => write!(f, "UndoRenameTab"),
+ Self::Run(run_command_action) => {
+ let mut run_block_serialized = format!("Run {:?}", run_command_action.command);
+ for arg in &run_command_action.args {
+ run_block_serialized.push_str(&format!(" \"{}\"", arg));
+ }
+ match (&run_command_action.cwd, run_command_action.direction) {
+ (Some(cwd), Some(direction)) => {
+ run_block_serialized.push_str(&format!(
+ "{{ cwd {:?}; direction \"{}\"; }}",
+ cwd, direction
+ ));
+ },
+ (None, Some(direction)) => {
+ run_block_serialized
+ .push_str(&format!("{{ direction \"{}\"; }}", direction));
+ },
+ (Some(cwd), None) => {
+ run_block_serialized.push_str(&format!("{{ cwd {:?}; }}", cwd));
+ },
+ (None, None) => {},
+ }
+ write!(f, "{}", run_block_serialized)
+ },
+ Self::Detach => write!(f, "Detach"),
+ Self::Copy => write!(f, "Copy"),
+ Self::Confirm => write!(f, "Confirm"),
+ Self::Deny => write!(f, "Deny"),
+ Self::SearchInput(bytes) => write!(
+ f,
+ "SearchInput {}",
+ bytes
+ .iter()
+ .map(|c| format!("{}", *c))
+ .collect::<Vec<String>>()
+ .join(" ")
+ ),
+ Self::Search(direction) => write!(f, "Search \"{}\"", direction),
+ Self::SearchToggleOption(option) => write!(f, "SearchToggleOption \"{}\"", option),
+ _ => Err(std::fmt::Error),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
+enum OldSearchDirection {
+ Down,
+ Up,
+}
+
+impl std::fmt::Display for OldSearchDirection {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::Down => write!(f, "Down"),
+ Self::Up => write!(f, "Up"),
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
+enum OldSearchOption {
+ CaseSensitivity,
+ WholeWord,
+ Wrap,
+}
+
+impl std::fmt::Display for OldSearchOption {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::CaseSensitivity => write!(f, "CaseSensitivity"),
+ Self::WholeWord => write!(f, "WholeWord"),
+ Self::Wrap => write!(f, "Wrap"),
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
+enum OldResizeDirection {
+ Left,
+ Right,
+ Up,
+ Down,
+ Increase,
+ Decrease,
+}
+
+impl std::fmt::Display for OldResizeDirection {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ Self::Left => write!(f, "Left"),
+ Self::Right => write!(f, "Right"),
+ Self::Up => write!(f, "Up"),
+ Self::Down => write!(f, "Down"),
+ Self::Increase => write!(f, "Increase"),
+ Self::Decrease => write!(f, "Decrease"),
+ }
+ }
+}
+
+#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Deserialize, Serialize)]
+struct OldPosition {
+ pub line: OldLine,
+ pub column: OldColumn,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd)]
+struct OldLine(pub isize);
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd)]
+struct OldColumn(pub usize);
+
+#[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)]
+struct OldRunCommandAction {
+ #[serde(rename = "cmd")]
+ pub command: PathBuf,
+ #[serde(default)]
+ pub args: Vec<String>,
+ #[serde(default)]
+ pub cwd: Option<PathBuf>,
+ #[serde(default)]
+ pub direction: Option<OldDirection>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+struct OldTabLayout {
+ #[serde(default)]
+ pub direction: OldDirection,
+ pub pane_name: Option<String>,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub parts: Vec<OldTabLayout>,
+ pub split_size: Option<OldSplitSize>,
+ #[serde(default)]
+ pub name: String,
+ pub focus: Option<bool>,
+ pub run: Option<OldRunFromYaml>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
+enum OldSplitSize {
+ #[serde(alias = "percent")]
+ Percent(u64), // 1 to 100
+ #[serde(alias = "fixed")]
+ Fixed(usize), // An absolute number of columns or rows
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+enum OldRunFromYaml {
+ #[serde(rename = "plugin")]
+ Plugin(OldRunPluginFromYaml),
+ #[serde(rename = "command")]
+ Command(OldRunCommand),
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+struct OldRunPluginFromYaml {
+ #[serde(default)]
+ pub _allow_exec_host_cmd: bool,
+ pub location: Url,
+}
+
+#[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)]
+pub struct OldRunCommand {
+ #[serde(alias = "cmd")]
+ pub command: PathBuf,
+ #[serde(default)]
+ pub args: Vec<String>,
+ #[serde(default)]
+ pub cwd: Option<PathBuf>,
+}
+
+// The unit test location.
+#[path = "./unit/convert_config_tests.rs"]
+#[cfg(test)]
+mod convert_config_test;
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 00000000..cd1aa66f
--- /dev/null
+++ b/zellij-client/src/old_config_converter/old_layout.rs
@@ -0,0 +1,557 @@
+// 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::old_config::{config_yaml_to_config_kdl, OldConfigFromYaml, OldRunCommand};
+use serde::{Deserialize, Serialize};
+use std::vec::Vec;
+use std::{fmt, path::PathBuf};
+use url::Url;
+
+fn pane_line(
+ pane_name: Option<&String>,
+ split_size: Option<OldSplitSize>,
+ focus: Option<bool>,
+ 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<OldSplitSize>,
+ focus: Option<bool>,
+ 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<OldSplitSize>,
+ focus: Option<bool>,
+ 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<OldSplitSize>,
+ focus: Option<bool>,
+ 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<OldSplitSize>,
+ focus: Option<bool>,
+ 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::<Vec<String>>()
+ .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<OldTabLayout>) -> 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<String, String> {
+ // 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 OldDirection {
+ #[serde(alias = "horizontal")]
+ Horizontal,
+ #[serde(alias = "vertical")]
+ Vertical,
+}
+
+impl fmt::Display for OldDirection {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ Self::Horizontal => write!(f, "Horizontal"),
+ Self::Vertical => write!(f, "Vertical"),
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
+pub enum OldSplitSize {
+ #[serde(alias = "percent")]
+ Percent(u64), // 1 to 100
+ #[serde(alias = "fixed")]
+ Fixed(usize), // An absolute number of columns or rows
+}
+
+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 OldRunFromYaml {
+ #[serde(rename = "plugin")]
+ Plugin(OldRunPluginFromYaml),
+ #[serde(rename = "command")]
+ Command(OldRunCommand),
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub struct OldRunPluginFromYaml {
+ #[serde(default)]
+ pub _allow_exec_host_cmd: bool,
+ pub location: Url,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[serde(default)]
+pub struct OldLayoutFromYamlIntermediate {
+ #[serde(default)]
+ pub template: OldLayoutTemplate,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub tabs: Vec<OldTabLayout>,
+ #[serde(default)]
+ pub session: OldSessionFromYaml,
+ #[serde(flatten)]
+ pub config: Option<OldConfigFromYaml>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
+#[serde(default)]
+pub struct OldLayoutFromYaml {
+ #[serde(default)]
+ pub session: OldSessionFromYaml,
+ #[serde(default)]
+ pub template: OldLayoutTemplate,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub tabs: Vec<OldTabLayout>,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
+pub struct OldSessionFromYaml {
+ pub name: Option<String>,
+ #[serde(default = "default_as_some_true")]
+ pub attach: Option<bool>,
+}
+
+fn default_as_some_true() -> Option<bool> {
+ Some(true)
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+pub struct OldLayoutTemplate {
+ pub direction: OldDirection,
+ #[serde(default)]
+ pub pane_name: Option<String>,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub parts: Vec<OldLayoutTemplate>,
+ #[serde(default)]
+ pub body: bool,
+ pub split_size: Option<OldSplitSize>,
+ pub focus: Option<bool>,
+ pub run: Option<OldRunFromYaml>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub struct OldTabLayout {
+ #[serde(default)]
+ pub direction: OldDirection,
+ pub pane_name: Option<String>,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub parts: Vec<OldTabLayout>,
+ pub split_size: Option<OldSplitSize>,
+ #[serde(default)]
+ pub name: String,
+ pub focus: Option<bool>,
+ pub run: Option<OldRunFromYaml>,
+}
+
+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 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 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<OldLayoutFromYamlIntermediate> for OldLayoutFromYaml {
+ fn from(layout_from_yaml_intermediate: OldLayoutFromYamlIntermediate) -> 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<OldLayoutFromYaml> for OldLayoutFromYamlIntermediate {
+ fn from(layout_from_yaml: OldLayoutFromYaml) -> 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 OldLayoutFromYamlIntermediate {
+ fn default() -> Self {
+ OldLayoutFromYaml::default().into()
+ }
+}
+
+impl Default for OldLayoutTemplate {
+ fn default() -> Self {
+ Self {
+ direction: OldDirection::Horizontal,
+ pane_name: None,
+ body: false,
+ borderless: false,
+ parts: vec![OldLayoutTemplate {
+ direction: OldDirection::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 OldDirection {
+ fn default() -> Self {
+ OldDirection::Horizontal
+ }
+}
+
+// The unit test location.
+#[path = "./unit/convert_layout_tests.rs"]
+#[cfg(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
new file mode 100644
index 00000000..444d8775
--- /dev/null
+++ b/zellij-client/src/old_config_converter/unit/convert_config_tests.rs
@@ -0,0 +1,126 @@
+use crate::old_config_converter::config_yaml_to_config_kdl;
+use insta::assert_snapshot;
+use std::path::PathBuf;
+use std::{fs::File, io::prelude::*};
+
+#[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, false)?;
+ 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, false)?;
+ 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, false)?;
+ 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, false)?;
+ 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, false)?;
+ 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, false)?;
+ 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, false)?;
+ 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, 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 00000000..30e689bb
--- /dev/null
+++ b/zellij-client/src/old_config_converter/unit/convert_layout_tests.rs
@@ -0,0 +1,142 @@
+use crate::old_config_converter::layout_yaml_to_layout_kdl;
+use insta::assert_snapshot;
+use std::path::PathBuf;
+use std::{fs::File, io::prelude::*};
+
+#[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/src/tests/fixtures/layouts/focus-tab-layout.yaml b/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml
index 1d019020..04fca4a5 100644
--- a/src/tests/fixtures/layouts/focus-tab-layout.yaml
+++ b/zellij-client/src/old_config_converter/unit/fixtures/multiple_tabs_layout.yaml
@@ -30,13 +30,11 @@ tabs:
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
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 00000000..1449aa71
--- /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-utils/assets/config/default.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml
index 2aecc6d8..cf0e5150 100644
--- a/zellij-utils/assets/config/default.yaml
+++ b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_config.yaml
@@ -573,13 +573,13 @@ plugins:
# Options:
# - detach (Default)
# - quit
-#on_force_close: quit
+# on_force_close: quit
# Send a request for a simplified ui (without arrow fonts) to plugins
# Options:
# - true
# - false (Default)
-#simplified_ui: true
+# simplified_ui: true
# Choose the path to the default shell that zellij will use for opening new panes
# Default: $SHELL
diff --git a/zellij-utils/assets/layouts/default.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml
index 0688e54b..0688e54b 100644
--- a/zellij-utils/assets/layouts/default.yaml
+++ b/zellij-client/src/old_config_converter/unit/fixtures/old_default_yaml_layout.yaml
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 00000000..797a3f22
--- /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 00000000..26006ba3
--- /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 00000000..819764cb
--- /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 00000000..d2839a64
--- /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 00000000..edf4af81
--- /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 00000000..5cf962b8
--- /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 00000000..fff9a528
--- /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/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 00000000..0d5f40b9
--- /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 00000000..7e6fa2cb
--- /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-utils/assets/layouts/strider.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml
index 26e1eba4..9e452574 100644
--- a/zellij-utils/assets/layouts/strider.yaml
+++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name.yaml
@@ -20,11 +20,5 @@ template:
location: "zellij:status-bar"
tabs:
- direction: Vertical
- parts:
- - direction: Horizontal
- split_size:
- Percent: 20
- run:
- plugin:
- location: "zellij:strider"
- - direction: Horizontal
+session:
+ name: foo
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml
index 8148fd20..1f33f1c3 100644
--- a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml
+++ b/zellij-client/src/old_config_converter/unit/fixtures/old_yaml_layout_with_session_name_and_attach_false.yaml
@@ -3,14 +3,16 @@ template:
direction: Horizontal
parts:
- direction: Vertical
+ borderless: true
split_size:
Fixed: 1
run:
plugin:
location: "zellij:tab-bar"
- - direction: Horizontal
+ - direction: Vertical
body: true
- direction: Vertical
+ borderless: true
split_size:
Fixed: 2
run:
@@ -18,15 +20,6 @@ template:
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
+session:
+ name: foo
+ attach: false
diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml b/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml
index 83f05076..91f4f63c 100644
--- a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml
+++ b/zellij-client/src/old_config_converter/unit/fixtures/run_htop_layout.yaml
@@ -1,17 +1,10 @@
---
-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:
@@ -19,3 +12,10 @@ tabs:
- 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 00000000..05d2798f
--- /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_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 00000000..34539e60
--- /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 00000000..c28f4afa
--- /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 00000000..5ce03e24
--- /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 00000000..d7ac6474
--- /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 00000000..f8a65133
--- /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 00000000..e805563a
--- /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 00000000..2b35143e
--- /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 00000000..461258e4
--- /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-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 00000000..94a9a4f3
--- /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 00000000..67cb8a60
--- /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 00000000..0c5145a1
--- /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 00000000..733e2aff
--- /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 00000000..ed6d2d6d
--- /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 00000000..a063d917
--- /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 00000000..a77bc525
--- /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 00000000..7b607256
--- /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 00000000..4a09faea
--- /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-client/src/sessions.rs b/zellij-client/src/sessions.rs
deleted file mode 100644
index 6a3c0799..00000000
--- 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 b3f17ad3..1f642534 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,
options::Options,
plugins::PluginsConfig,
},
@@ -61,7 +61,7 @@ pub enum ServerInstruction {
ClientAttributes,
Box<CliArgs>,
Box<Options>,
- Box<LayoutFromYaml>,
+ Box<Layout>,
ClientId,
Option<PluginsConfig>,
),
@@ -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();
+ }
}
}
@@ -316,7 +324,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
})
});
- let spawn_tabs = |tab_layout| {
+ let spawn_tabs = |tab_layout, tab_name| {
session_data
.read()
.unwrap()
@@ -326,33 +334,32 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, 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.has_tabs() {
+ for (tab_name, tab_layout) in layout.tabs() {
+ spawn_tabs(Some(tab_layout.clone()), 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();
}
} else {
- spawn_tabs(None);
+ spawn_tabs(None, None);
}
session_data
.read()
@@ -594,7 +601,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
pub struct SessionOptions {
pub opts: Box<CliArgs>,
pub config_options: Box<Options>,
- pub layout: Box<LayoutFromYaml>,
+ pub layout: Box<Layout>,
pub plugins: Option<PluginsConfig>,
}
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index d8b6c605..8b1beeac 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/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index 03fa0c06..81911e12 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<Item = &PaneId> {
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,12 @@ 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 +271,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 +284,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 +298,13 @@ 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 +316,13 @@ 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,15 +461,18 @@ impl TiledPanes {
*display_area,
*viewport,
);
- if pane_grid.layout(Direction::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!!!");
- }
- if pane_grid.layout(Direction::Vertical, rows).is_ok() {
+ 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;
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 6819a0a8..8667e2f6 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<Grid, String> {
+ fn solve(&mut self, direction: SplitDirection, space: usize) -> Result<Grid, String> {
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<Span> = self
.panes
@@ -169,7 +169,7 @@ impl<'a> PaneResizer<'a> {
bounds
}
- fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec<Span> {
+ fn spans_in_boundary(&self, direction: SplitDirection, boundary: (usize, usize)) -> Vec<Span> {
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 6bb90e9d..b8efc5ec 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<PaneId> = result_panes.iter().map(|t| t.pid()).collect();
(right_resize_border, pane_ids)
}
- fn ids_are_flexible(&self, direction: Direction, pane_ids: Option<Vec<PaneId>>) -> bool {
+ fn ids_are_flexible(&self, direction: SplitDirection, pane_ids: Option<Vec<PaneId>>) -> 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,36 @@ impl<'a> TiledPaneGrid<'a> {
}
None
}
- fn find_panes_to_grow(&self, id: PaneId) -> Option<(Vec<PaneId>, Direction)> {
+ fn find_panes_to_grow(&self, id: PaneId) -> Option<(Vec<PaneId>, 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 +1602,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 +1619,7 @@ impl<'a> TiledPaneGrid<'a> {
pub fn find_room_for_new_pane(
&self,
cursor_height_width_ratio: Option<usize>,
- ) -> Option<(PaneId, Direction)> {
+ ) -> Option<(PaneId, SplitDirection)> {
let panes = self.panes.borrow();
let pane_sequence: Vec<(&PaneId, &&mut Box<dyn Pane>)> =
panes.iter().filter(|(_, p)| p.selectable()).collect();
@@ -1643,9 +1648,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 +1660,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 f1489ea6..df4d270a 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, LayoutFromYaml, Run, TabLayout},
+ layout::{Layout, PaneLayout, Run},
},
};
@@ -33,7 +33,12 @@ pub(crate) enum PtyInstruction {
SpawnTerminalHorizontally(Option<TerminalAction>, ClientId),
UpdateActivePane(Option<PaneId>, ClientId),
GoToTab(TabIndex, ClientId),
- NewTab(Option<TerminalAction>, Option<TabLayout>, ClientId),
+ NewTab(
+ Option<TerminalAction>,
+ Option<PaneLayout>,
+ Option<String>,
+ ClientId,
+ ), // the String is the tab name
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
Exit,
@@ -65,9 +70,7 @@ pub(crate) struct Pty {
default_editor: Option<PathBuf>,
}
-use std::convert::TryFrom;
-
-pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) {
+pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
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,20 +139,12 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) {
.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())
- }
- });
-
- let merged_layout = layout.template.clone().insert_tab_layout(tab_layout);
- let layout: Layout =
- Layout::try_from(merged_layout).unwrap_or_else(|err| panic!("{}", err));
-
- pty.spawn_terminals_for_layout(layout, terminal_action.clone(), client_id);
+ PtyInstruction::NewTab(terminal_action, tab_layout, tab_name, 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
@@ -271,7 +266,7 @@ impl Pty {
}
pub fn spawn_terminals_for_layout(
&mut self,
- layout: Layout,
+ layout: PaneLayout,
default_shell: Option<TerminalAction>,
client_id: ClientId,
) {
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index d82ec42e..75230488 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,
@@ -142,7 +142,8 @@ 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();
},
@@ -248,6 +249,108 @@ 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::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
@@ -303,11 +406,13 @@ 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-server/src/screen.rs b/zellij-server/src/screen.rs
index ef4b4f60..012cc557 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -9,7 +9,7 @@ use std::str;
use zellij_utils::errors::prelude::*;
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, position::Position};
use crate::panes::alacritty_functions::xparse_color;
use crate::panes::terminal_character::AnsiCode;
@@ -29,7 +29,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
@@ -41,6 +41,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) {
@@ -54,6 +55,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)]
@@ -64,6 +86,8 @@ pub enum ScreenInstruction {
OpenInPlaceEditor(PaneId, ClientId),
TogglePaneEmbedOrFloating(ClientId),
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
+ ShowFloatingPanes(ClientId),
+ HideFloatingPanes(ClientId),
HorizontalSplit(PaneId, ClientId),
VerticalSplit(PaneId, ClientId),
WriteCharacter(Vec<u8>, ClientId),
@@ -107,7 +131,7 @@ pub enum ScreenInstruction {
ClosePane(PaneId, Option<ClientId>),
UpdatePaneName(Vec<u8>, ClientId),
UndoRenamePane(ClientId),
- NewTab(Layout, Vec<RawFd>, ClientId),
+ NewTab(PaneLayout, Vec<RawFd>, ClientId),
SwitchTabNext(ClientId),
SwitchTabPrev(ClientId),
ToggleActiveSyncTab(ClientId),
@@ -122,6 +146,7 @@ pub enum ScreenInstruction {
TerminalForegroundColor(String),
TerminalColorRegisters(Vec<(usize, String)>),
ChangeMode(ModeInfo, ClientId),
+ ChangeModeForAllClients(ModeInfo),
LeftClick(Position, ClientId),
RightClick(Position, ClientId),
MiddleClick(Position, ClientId),
@@ -157,6 +182,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,
@@ -223,6 +250,9 @@ 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,
@@ -485,35 +515,51 @@ impl Screen {
/// Sets this [`Screen`]'s active [`Tab`] to the next tab.
pub fn switch_tab_next(&mut self, client_id: ClientId) -> Result<()> {
- 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 {
+ 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();
+ return self.switch_active_tab(new_tab_pos, client_id);
+ } else {
+ log::error!("Active tab not found for client_id: {:?}", client_id);
+ }
log::error!("Active tab not found for client id: {client_id:?}");
- Ok(())
}
+ Ok(())
}
/// Sets this [`Screen`]'s active [`Tab`] to the previous tab.
pub fn switch_tab_prev(&mut self, client_id: ClientId) -> Result<()> {
- 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 {
+ 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
+ };
+
+ return self.switch_active_tab(new_tab_pos, client_id);
+ } else {
+ log::error!("Active tab not found for client_id: {:?}", client_id);
+ }
log::error!("Active tab not found for client id: {client_id:?}");
- Ok(())
}
+ Ok(())
}
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<()> {
@@ -555,13 +601,22 @@ impl Screen {
// Closes the client_id's focused tab
pub fn close_tab(&mut self, client_id: ClientId) -> Result<()> {
let err_context = || format!("failed to close tab for client {client_id:?}");
-
- let active_tab_index = *self
- .active_tab_indices
- .get(&client_id)
- .with_context(err_context)?;
- self.close_tab_at_index(active_tab_index)
- .with_context(err_context)
+ let client_id = if self.get_active_tab(client_id).is_some() {
+ Some(client_id)
+ } else {
+ self.get_first_client_id()
+ };
+ match client_id {
+ Some(client_id) => {
+ let active_tab_index = *self
+ .active_tab_indices
+ .get(&client_id)
+ .with_context(err_context)?;
+ self.close_tab_at_index(active_tab_index)
+ .with_context(err_context)
+ },
+ None => Ok(()),
+ }
}
pub fn resize_to_screen(&mut self, new_screen_size: Size) -> Result<()> {
@@ -656,6 +711,10 @@ impl Screen {
}
}
+ pub fn get_first_client_id(&self) -> Option<ClientId> {
+ 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) -> Result<Option<&Tab>> {
@@ -696,12 +755,18 @@ impl Screen {
/// and switching to it.
pub fn new_tab(
&mut self,
- layout: Layout,
+ layout: PaneLayout,
new_pids: Vec<RawFd>,
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() {
+ first_client_id
+ } else {
+ client_id
+ };
let err_context = || format!("failed to create new tab for client {client_id:?}");
-
let tab_index = self.get_new_tab_index();
let position = self.tabs.len();
let mut tab = Tab::new(
@@ -854,44 +919,62 @@ impl Screen {
}
pub fn update_active_tab_name(&mut self, buf: Vec<u8>, client_id: ClientId) -> Result<()> {
- let s = str::from_utf8(&buf)
- .with_context(|| format!("failed to construct tab name from buf: {buf:?}"))?;
- 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().with_context(|| {
- format!("failed to update active tabs name for client id: {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:?}");
- Ok(())
+ self.get_first_client_id()
+ };
+ match client_id {
+ Some(client_id) => {
+ let s = str::from_utf8(&buf)
+ .with_context(|| format!("failed to construct tab name from buf: {buf:?}"))?;
+ 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().with_context(|| {
+ format!("failed to update active tabs name for client id: {client_id:?}")
+ })
+ } else {
+ log::error!("Active tab not found for client id: {client_id:?}");
+ Ok(())
+ }
+ },
+ None => Ok(()),
}
}
-
pub fn undo_active_rename_tab(&mut self, client_id: ClientId) -> Result<()> {
- 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()
- .context("failed to undo renaming of active tab")?;
- }
- Ok(())
+ 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:?}");
- Ok(())
+ self.get_first_client_id()
+ };
+ match client_id {
+ Some(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()
+ .context("failed to undo renaming of active tab")?;
+ }
+ } else {
+ log::error!("Active tab not found for client id: {client_id:?}");
+ }
+ Ok(())
+ },
+ None => Ok(()),
}
}
@@ -941,26 +1024,49 @@ impl Screen {
tab.mark_active_pane_for_rerender(client_id);
}
}
-
- pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) -> Result<()> {
- 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)
- .context("failed to move focus left")?;
+ pub fn change_mode_for_all_clients(&mut self, mode_info: ModeInfo) {
+ let connected_client_ids: Vec<ClientId> = 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) -> Result<()> {
+ 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)
+ .context("failed to move focus left")?;
+ }
+ } else {
+ log::error!("Active tab not found for client id: {:?}", client_id);
+ }
}
Ok(())
}
pub fn move_focus_right_or_next_tab(&mut self, client_id: ClientId) -> Result<()> {
- 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)
- .context("failed to move focus right")?;
- }
+ 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)
+ .context("failed to move focus right")?;
+ }
+ } else {
+ log::error!("Active tab not found for client id: {:?}", client_id);
+ }
}
Ok(())
}
@@ -996,7 +1102,6 @@ pub(crate) fn screen_thread_main(
client_attributes: ClientAttributes,
config_options: Box<Options>,
) -> Result<()> {
- // let mut scrollbacks: HashMap<String, PaneId> = 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);
@@ -1046,8 +1151,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!(screen, client_id, |tab: &mut Tab| 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) {
@@ -1071,89 +1179,168 @@ pub(crate) fn screen_thread_main(
screen.render()?;
},
ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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!(screen, client_id, |tab: &mut Tab| 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()
+ );
+ 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
- .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!(screen, client_id, |tab: &mut Tab| 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!(screen, client_id, |tab: &mut Tab| {
- 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!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::ResizeRight(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::ResizeDown(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::ResizeIncrease(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::ResizeDecrease(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::SwitchFocus(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::FocusNextPane(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::FocusPreviousPane(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MoveFocusLeft(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id) => {
screen.move_focus_left_or_previous_tab(client_id)?;
@@ -1161,14 +1348,22 @@ pub(crate) fn screen_thread_main(
screen.render()?;
},
ScreenInstruction::MoveFocusDown(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MoveFocusRight(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MoveFocusRightOrNextTab(client_id) => {
screen.move_focus_right_or_next_tab(client_id)?;
@@ -1176,99 +1371,184 @@ pub(crate) fn screen_thread_main(
screen.render()?;
},
ScreenInstruction::MoveFocusUp(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::DumpScreen(file, client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::EditScrollback(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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!(screen, client_id, |tab: &mut Tab| 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.unblock_input()?;
screen.render()?;
},
ScreenInstruction::MovePane(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MovePaneDown(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MovePaneUp(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MovePaneRight(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::MovePaneLeft(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::ScrollUpAt(point, client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::ScrollDown(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::ScrollDownAt(point, client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::ScrollToBottom(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::PageScrollUp(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::PageScrollDown(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::HalfPageScrollUp(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::HalfPageScrollDown(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::ClearScroll(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::CloseFocusedPane(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| tab
- .close_focused_pane(client_id));
- screen.update_tabs()?; // update_tabs eventually calls render through the plugin thread
+ 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::SetSelectable(id, selectable, tab_index) => {
screen.get_indexed_tab_mut(tab_index).map_or_else(
@@ -1299,22 +1579,36 @@ pub(crate) fn screen_thread_main(
},
}
screen.update_tabs()?;
+ screen.unblock_input()?;
},
ScreenInstruction::UpdatePaneName(c, client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::UndoRenamePane(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::TogglePaneFrames => {
screen.draw_pane_frames = !screen.draw_pane_frames;
@@ -1322,6 +1616,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)?;
@@ -1344,9 +1639,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()?;
@@ -1354,10 +1654,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) => {
@@ -1379,35 +1681,49 @@ 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!(screen, client_id, |tab: &mut Tab| 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()?;
+ 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
@@ -1488,39 +1804,63 @@ pub(crate) fn screen_thread_main(
screen.unblock_input()?;
},
ScreenInstruction::UpdateSearch(c, client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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!(screen, client_id, |tab: &mut Tab| 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!(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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::SearchToggleCaseSensitivity(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::SearchToggleWrap(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
ScreenInstruction::SearchToggleWholeWord(client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| 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()?;
+ screen.unblock_input()?;
},
}
}
Ok(())
}
-#[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 31f3fa1c..883caf0d 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, Run},
parse_keys,
},
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
@@ -454,7 +454,7 @@ impl Tab {
pub fn apply_layout(
&mut self,
- layout: Layout,
+ layout: PaneLayout,
new_pids: Vec<RawFd>,
tab_index: usize,
client_id: ClientId,
@@ -478,7 +478,7 @@ impl Tab {
let mut new_pids = new_pids.iter();
let mut focus_pane_id: Option<PaneId> = 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);
}
@@ -498,7 +498,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
@@ -513,7 +513,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(),
@@ -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<ClientId>) {
self.close_down_to_max_terminals();
if self.floating_panes.panes_are_visible() {
@@ -2036,7 +2044,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 {
@@ -2044,13 +2051,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(
@@ -2066,7 +2071,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/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 00000000..2a3dc369
--- /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 00000000..2d185efe
--- /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 00000000..6ac556ba
--- /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 14a9c9e1..3b26bc26 100644
--- a/zellij-server/src/tab/unit/tab_integration_tests.rs
+++ b/zellij-server/src/tab/unit/tab_integration_tests.rs
@@ -9,12 +9,11 @@ use crate::{
thread_bus::ThreadSenders,
ClientId,
};
-use std::convert::TryInto;
use std::path::PathBuf;
use zellij_utils::channels::Receiver;
use zellij_utils::envs::set_session_name;
use zellij_utils::errors::ErrorContext;
-use zellij_utils::input::layout::LayoutTemplate;
+use zellij_utils::input::layout::{Layout, PaneLayout};
use zellij_utils::ipc::IpcReceiverWithContext;
use zellij_utils::pane_size::{Size, SizeInPixels};
use zellij_utils::position::Position;
@@ -213,12 +212,62 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(
- LayoutTemplate::default().try_into().unwrap(),
- vec![1],
+ tab.apply_layout(PaneLayout::default(), vec![1], index, client_id);
+ 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, "layout_file_name".into()).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
}
@@ -271,7 +320,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(),
+ PaneLayout::default(),
vec![1],
index,
client_id,
@@ -329,12 +379,7 @@ fn create_new_tab_with_sixel_support(
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(
- LayoutTemplate::default().try_into().unwrap(),
- vec![1],
- index,
- client_id,
- );
+ tab.apply_layout(PaneLayout::default(), vec![1], index, client_id);
tab
}
@@ -2051,6 +2096,110 @@ 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);
+}
+
+#[test]
fn pane_bracketed_paste_ignored_when_not_in_bracketed_paste_mode() {
// regression test for: https://github.com/zellij-org/zellij/issues/1687
let size = Size {
diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs
index 8a307250..60604b00 100644
--- a/zellij-server/src/tab/unit/tab_tests.rs
+++ b/zellij-server/src/tab/unit/tab_tests.rs
@@ -7,9 +7,8 @@ use crate::{
thread_bus::ThreadSenders,
ClientId,
};
-use std::convert::TryInto;
use std::path::PathBuf;
-use zellij_utils::input::layout::LayoutTemplate;
+use zellij_utils::input::layout::PaneLayout;
use zellij_utils::ipc::IpcReceiverWithContext;
use zellij_utils::pane_size::{Size, SizeInPixels};
@@ -132,12 +131,7 @@ fn create_new_tab(size: Size) -> Tab {
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(
- LayoutTemplate::default().try_into().unwrap(),
- vec![1],
- index,
- client_id,
- );
+ tab.apply_layout(PaneLayout::default(), vec![1], index, client_id);
tab
}
@@ -183,12 +177,7 @@ fn create_new_tab_with_cell_size(
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(
- LayoutTemplate::default().try_into().unwrap(),
- vec![1],
- index,
- client_id,
- );
+ tab.apply_layout(PaneLayout::default(), vec![1], index, client_id);
tab
}
diff --git a/zellij-server/src/thread_bus.rs b/zellij-server/src/thread_bus.rs
index 1bb00c09..21ddecfe 100644
--- a/zellij-server/src/thread_bus.rs
+++ b/zellij-server/src/thread_bus.rs
@@ -147,6 +147,12 @@ impl<T> Bus<T> {
}
}
#[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 00000000..ea1f01e7
--- /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 28aec39c..16b962f1 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -1,30 +1,129 @@
-use super::{CopyOptions, Screen, ScreenInstruction};
+use super::{screen_thread_main, CopyOptions, Screen, ScreenInstruction};
use crate::panes::PaneId;
use crate::{
+ channels::SenderWithContext,
os_input_output::{AsyncReader, Pid, ServerOsApi},
+ route::route_action,
thread_bus::Bus,
- ClientId,
+ ClientId, ServerInstruction, SessionMetaData, ThreadSenders,
};
-use std::convert::TryInto;
+use insta::assert_snapshot;
use std::path::PathBuf;
+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::LayoutTemplate;
+use zellij_utils::input::layout::{PaneLayout, SplitDirection};
+use zellij_utils::input::options::Options;
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 zellij_utils::ipc::{ClientAttributes, PixelDimensions};
+use crate::{pty::PtyInstruction, wasm_vm::PluginInstruction};
+use zellij_utils::ipc::PixelDimensions;
use zellij_utils::nix;
-
use zellij_utils::{
- data::{ModeInfo, Palette},
+ channels::{self, ChannelWithContext, Receiver},
+ data::{InputMode, ModeInfo, Palette, PluginCapabilities},
interprocess::local_socket::LocalSocketStream,
- ipc::{ClientToServerMsg, ServerToClientMsg},
+ ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg},
};
-#[derive(Clone)]
-struct FakeInputOutput {}
+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 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<Item = &'a ServerInstruction>,
+ 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<Mutex<HashMap<String, String>>>,
+ server_to_client_messages: Arc<Mutex<HashMap<ClientId, Vec<ServerToClientMsg>>>>,
+}
impl ServerOsApi for FakeInputOutput {
fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
@@ -61,10 +160,16 @@ 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,
@@ -82,21 +187,27 @@ impl ServerOsApi for FakeInputOutput {
fn get_cwd(&self, _pid: Pid) -> Option<PathBuf> {
unimplemented!()
}
- fn write_to_file(&mut self, _: String, _: Option<String>) {
- unimplemented!()
+ fn write_to_file(&mut self, contents: String, filename: Option<String>) {
+ if let Some(filename) = filename {
+ self.fake_filesystem
+ .lock()
+ .unwrap()
+ .insert(filename, contents);
+ }
}
}
fn create_new_screen(size: Size) -> Screen {
let mut bus: Bus<ScreenInstruction> = 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,
..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();
@@ -112,14 +223,199 @@ fn create_new_screen(size: Size) -> Screen {
)
}
+struct MockScreen {
+ pub main_client_id: u16,
+ pub pty_receiver: Option<Receiver<(PtyInstruction, ErrorContext)>>,
+ pub pty_writer_receiver: Option<Receiver<(PtyWriteInstruction, ErrorContext)>>,
+ pub screen_receiver: Option<Receiver<(ScreenInstruction, ErrorContext)>>,
+ pub server_receiver: Option<Receiver<(ServerInstruction, ErrorContext)>>,
+ pub plugin_receiver: Option<Receiver<(PluginInstruction, ErrorContext)>>,
+ pub to_screen: SenderWithContext<ScreenInstruction>,
+ pub to_pty: SenderWithContext<PtyInstruction>,
+ pub to_plugin: SenderWithContext<PluginInstruction>,
+ pub to_server: SenderWithContext<ServerInstruction>,
+ pub to_pty_writer: SenderWithContext<PtyWriteInstruction>,
+ 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<PaneLayout>) -> 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 || {
+ set_var("ZELLIJ_SESSION_NAME", "zellij-test");
+ screen_thread_main(
+ screen_bus,
+ None,
+ client_attributes,
+ Box::new(config_options),
+ )
+ .expect("TEST")
+ })
+ .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<std::thread::JoinHandle<()>>) {
+ 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<ServerInstruction> =
+ channels::bounded(50);
+ let to_server = SenderWithContext::new(to_server);
+
+ let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> =
+ channels::unbounded();
+ let to_screen = SenderWithContext::new(to_screen);
+
+ let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> =
+ channels::unbounded();
+ let to_plugin = SenderWithContext::new(to_plugin);
+ let (to_pty, pty_receiver): ChannelWithContext<PtyInstruction> = channels::unbounded();
+ let to_pty = SenderWithContext::new(to_pty);
+
+ let (to_pty_writer, pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> =
+ 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(
- LayoutTemplate::default().try_into().unwrap(),
- vec![pid],
- client_id,
- )
+ .new_tab(PaneLayout::default(), vec![pid], client_id)
.expect("TEST");
}
@@ -572,3 +868,1616 @@ fn attach_after_first_tab_closed() {
screen.remove_client(1).expect("TEST");
screen.add_client(1).expect("TEST");
}
+
+// 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,
+ 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,
+ 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_and_cwd() {
+ 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()),
+ 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_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]);
+ 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 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()
+ ))
+}
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 00000000..2df4565e
--- /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 00000000..c8e5e9a9
--- /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 00000000..163f7b35
--- /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 00000000..6c5fd6ab
--- /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 00000000..eea7656b
--- /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 00000000..b4240eb0
--- /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 00000000..eea7656b
--- /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 00000000..d4fce111
--- /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 00000000..eea7656b
--- /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 00000000..f6af649c
--- /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 00000000..d4fce111
--- /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 00000000..6eb6814d
--- /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 00000000..776d37ff
--- /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 00000000..9d4ecb19
--- /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 00000000..1d793cf6
--- /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 00000000..74460796
--- /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 00000000..f0d453eb
--- /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 00000000..ae95b51b
--- /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 00000000..74460796
--- /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 00000000..c8861240
--- /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 00000000..89f17aa7
--- /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 00000000..1189125a
--- /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 00000000..c8861240
--- /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 00000000..53beab4a
--- /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: 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"})), 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_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 00000000..6eb10760
--- /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 00000000..a2f36767
--- /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 00000000..6eb10760
--- /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 00000000..1688290e
--- /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 00000000..6eb10760
--- /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 00000000..e8b27b15
--- /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 00000000..1688290e
--- /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 00000000..0f0a9e25
--- /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 00000000..884173f4
--- /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 00000000..8118d806
--- /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 00000000..884173f4
--- /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 00000000..63ab9f8d
--- /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 00000000..d03e1da4
--- /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 00000000..4fdd411b
--- /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 00000000..f0b88e27
--- /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 00000000..8bc6c44b
--- /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 00000000..d38454b4
--- /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 00000000..7a6fd48a
--- /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 00000000..400b84d7
--- /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 00000000..97cb88db
--- /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 00000000..7a6fd48a
--- /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 00000000..208d683f
--- /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 00000000..407ab4bb
--- /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 00000000..eacdaba2
--- /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 00000000..208d683f
--- /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 00000000..14158fbd
--- /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 00000000..d4fc90dd
--- /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 00000000..4c454d89
--- /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 00000000..1c054e3a
--- /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_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 00000000..38b18ce7
--- /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_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 00000000..29b09a8b
--- /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: 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), 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 00000000..eedc6c50
--- /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: 1566
+expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
+---
+[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
new file mode 100644
index 00000000..de7a2465
--- /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: 1599
+expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
+---
+[SpawnTerminalVertically(None, 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_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 00000000..53a02426
--- /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 00000000..5f676ab7
--- /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,55 @@
+---
+source: zellij-server/src/./unit/screen_tests.rs
+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,
+)
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 00000000..f44abd51
--- /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 00000000..6c8d7629
--- /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 00000000..f44abd51
--- /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 00000000..fdac85da
--- /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 00000000..f44abd51
--- /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 00000000..8e7e4eae
--- /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 00000000..fdac85da
--- /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 00000000..dd4795d3
--- /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 00000000..38ae6619
--- /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 00000000..73c58989
--- /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 00000000..5de916b8
--- /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 00000000..fc4136a6
--- /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 00000000..19f6d030
--- /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 00000000..15f71daf
--- /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 00000000..481e5b23
--- /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 00000000..328d4b3a
--- /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 00000000..a59f6869
--- /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 00000000..412016df
--- /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 00000000..e2ff3ec6
--- /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 00000000..412016df
--- /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 00000000..b0d42634
--- /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 00000000..412016df
--- /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 00000000..648f833d
--- /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 00000000..b0d42634
--- /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 00000000..91d02a51
--- /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 00000000..da90407d
--- /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 00000000..42794790
--- /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 00000000..24b8735a
--- /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 00000000..40b651ae
--- /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: 2528
+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-test",
+ ),
+ },
+ ),
+ ),
+ 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-test",
+ ),
+ },
+ ),
+ ),
+ 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-test",
+ ),
+ },
+ ),
+ ),
+ 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-test",
+ ),
+ },
+ ),
+ ),
+ 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 00000000..da2e2a7b
--- /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 00000000..4325fbb4
--- /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 00000000..3b34c47d
--- /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 00000000..4d0118e1
--- /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 00000000..ce17e618
--- /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 00000000..eea8c7f4
--- /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 00000000..e2c021ed
--- /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 00000000..9765846d
--- /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 00000000..e2c021ed
--- /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 00000000..eea8c7f4
--- /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 00000000..47fdf2d7
--- /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 00000000..798b4720
--- /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 00000000..fd5c4f89
--- /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 00000000..7a2e2371
--- /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 00000000..8e073396
--- /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 00000000..657ed3e8
--- /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 00000000..60f03824
--- /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 00000000..92ac7a86
--- /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 00000000..92163142
--- /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 00000000..98e19e67
--- /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 00000000..cd8a3061
--- /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 00000000..d61adfbc
--- /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 00000000..a2ef5fa2
--- /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 00000000..9c7f4cf7
--- /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 00000000..94eef91e
--- /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 00000000..49e3d8fb
--- /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 00000000..8597c630
--- /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 00000000..e20094c2
--- /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 00000000..105baa7f
--- /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 00000000..84f72323
--- /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 00000000..105baa7f
--- /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 00000000..2b28bb3a
--- /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 00000000..b7d2d258
--- /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 00000000..d7a84628
--- /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 00000000..25e1f8df
--- /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 00000000..8ee7c1cb
--- /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 00000000..a18e57fb
--- /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 00000000..0cffa2c9
--- /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 00000000..81d9b259
--- /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 00000000..3ee6542f
--- /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 00000000..0f07af8a
--- /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 00000000..626bca74
--- /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 00000000..2e0fc915
--- /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 00000000..395ce350
--- /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 00000000..2ca593dd
--- /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 00000000..e8307ea9
--- /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 00000000..f8d04616
--- /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 00000000..37c5f371
--- /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 00000000..a65e528e
--- /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 00000000..f2b26455
--- /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: 2264
+expression: "format!(\"{}\", snapshot_count)"
+---
+4
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-6.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-6.snap
new file mode 100644
index 00000000..edd72e56
--- /dev/null
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_pane_action-6.snap
@@ -0,0 +1,6 @@
+---
+source: zellij-server/src/./unit/screen_tests.rs
+assertion_line: 2260
+expression: "format!(\"{}\", snapshot_count)"
+---
+5
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 00000000..3457c90d
--- /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 00000000..dbe2c85a
--- /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: 2571
+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-test",
+ ),
+ },
+ ),
+ ),
+ 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-test",
+ ),
+ },
+ ),
+ ),
+ 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-test",
+ ),
+ },
+ ),
+ ),
+ 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-test",
+ ),
+ },
+ ),
+ ),
+ 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 00000000..9421b96e
--- /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: 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-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]
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 00000000..32ae2a50
--- /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 00000000..90ade0ee
--- /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/Cargo.toml b/zellij-utils/Cargo.toml
index 4bbd0897..02fad102 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"
@@ -36,6 +35,7 @@ unicode-width = "0.1.8"
miette = { version = "3.3.0", features = ["fancy"] }
regex = "1.5.5"
tempfile = "3.2.0"
+kdl = { version = "4.5.0", features = ["span"] }
#[cfg(not(target_family = "wasm"))]
[target.'cfg(not(target_family = "wasm"))'.dependencies]
@@ -46,6 +46,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/config/default.kdl b/zellij-utils/assets/config/default.kdl
new file mode 100644
index 00000000..227dea43
--- /dev/null
+++ b/zellij-utils/assets/config/default.kdl
@@ -0,0 +1,294 @@
+keybinds {
+ normal {
+ // uncomment this and adjust key if using copy_on_select=false
+ // bind "Alt c" { Copy; }
+ }
+ locked {
+ bind "Ctrl g" { SwitchToMode "Normal"; }
+ }
+ resize {
+ 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"; }
+ }
+ pane {
+ bind "Ctrl p" { SwitchToMode "Normal"; }
+ 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 "c" { SwitchToMode "RenamePane"; PaneNameInput 0;}
+ }
+ move {
+ 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"; }
+ }
+ tab {
+ bind "Ctrl t" { SwitchToMode "Normal"; }
+ bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; }
+ 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; }
+ }
+ scroll {
+ bind "Ctrl s" { SwitchToMode "Normal"; }
+ bind "e" { EditScrollback; SwitchToMode "Normal"; }
+ bind "s" { SwitchToMode "EnterSearch"; SearchInput 0; }
+ bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; }
+ 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; }
+ // uncomment this and adjust key if using copy_on_select=false
+ // bind "Alt c" { Copy; }
+ }
+ search {
+ bind "Ctrl s" { SwitchToMode "Normal"; }
+ bind "Ctrl c" { ScrollToBottom; SwitchToMode "Normal"; }
+ 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 "n" { Search "down"; }
+ bind "p" { Search "up"; }
+ bind "c" { SearchToggleOption "CaseSensitivity"; }
+ bind "w" { SearchToggleOption "Wrap"; }
+ bind "o" { SearchToggleOption "WholeWord"; }
+ }
+ entersearch {
+ bind "Ctrl c" "Esc" { SwitchToMode "Scroll"; }
+ bind "Enter" { SwitchToMode "Search"; }
+ }
+ renametab {
+ bind "Ctrl c" { SwitchToMode "Normal"; }
+ bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; }
+ }
+ renamepane {
+ bind "Ctrl c" { SwitchToMode "Normal"; }
+ bind "Esc" { UndoRenamePane; SwitchToMode "Pane"; }
+ }
+ session {
+ bind "Ctrl o" { SwitchToMode "Normal"; }
+ bind "Ctrl s" { SwitchToMode "Scroll"; }
+ bind "d" { Detach; }
+ }
+ tmux {
+ bind "[" { SwitchToMode "Scroll"; }
+ 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 "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 "Alt =" "Alt +" { Resize "Increase"; }
+ bind "Alt -" { Resize "Decrease"; }
+ }
+ 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"; }
+ }
+}
+
+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:
+// - 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
+
+// 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
+// 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 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"
diff --git a/zellij-utils/assets/layouts/compact.kdl b/zellij-utils/assets/layouts/compact.kdl
new file mode 100644
index 00000000..7871ad40
--- /dev/null
+++ b/zellij-utils/assets/layouts/compact.kdl
@@ -0,0 +1,6 @@
+layout {
+ pane
+ pane size=1 borderless=true {
+ plugin location="zellij:compact-bar"
+ }
+}
diff --git a/zellij-utils/assets/layouts/compact.yaml b/zellij-utils/assets/layouts/compact.yaml
deleted file mode 100644
index d20ff08a..00000000
--- 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
new file mode 100644
index 00000000..4434b29b
--- /dev/null
+++ b/zellij-utils/assets/layouts/default.kdl
@@ -0,0 +1,9 @@
+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-utils/assets/layouts/disable-status-bar.kdl b/zellij-utils/assets/layouts/disable-status-bar.kdl
new file mode 100644
index 00000000..5eca5b0c
--- /dev/null
+++ b/zellij-utils/assets/layouts/disable-status-bar.kdl
@@ -0,0 +1,6 @@
+layout {
+ pane size=1 borderless=true {
+ plugin location="zellij:tab-bar"
+ }
+ pane
+}
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 10779398..00000000
--- 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.kdl b/zellij-utils/assets/layouts/no-plugins.kdl
new file mode 100644
index 00000000..45535924
--- /dev/null
+++ b/zellij-utils/assets/layouts/no-plugins.kdl
@@ -0,0 +1 @@
+layout
diff --git a/zellij-utils/assets/layouts/no-plugins.yaml b/zellij-utils/assets/layouts/no-plugins.yaml
deleted file mode 100644
index 808d6860..00000000
--- 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.kdl b/zellij-utils/assets/layouts/strider.kdl
new file mode 100644
index 00000000..dbe8d077
--- /dev/null
+++ b/zellij-utils/assets/layouts/strider.kdl
@@ -0,0 +1,19 @@
+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
+ }
+ }
+}
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index a947f943..24616bd1 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -1,6 +1,8 @@
+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,
};
use clap::{Parser, Subcommand};
@@ -46,6 +48,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
@@ -110,6 +121,137 @@ pub enum Sessions {
yes: bool,
},
/// Send actions to a specific session
- #[cfg(feature = "unstable")]
- Action { action: Option<String> },
+ #[clap(visible_alias = "ac")]
+ #[clap(subcommand)]
+ Action(CliAction),
+ /// Send actions to a specific session
+ #[clap(visible_alias = "c")]
+ Command {
+ command: Option<String>,
+ #[clap(short, long, value_parser, conflicts_with("floating"))]
+ direction: Option<Direction>,
+ #[clap(long, value_parser)]
+ cwd: Option<PathBuf>,
+ #[clap(short, long, value_parser, default_missing_value("true"))]
+ floating: Option<bool>,
+ },
+ /// 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<usize>,
+ #[clap(short, long, value_parser, conflicts_with("floating"))]
+ direction: Option<Direction>,
+ #[clap(short, long, value_parser, default_missing_value("true"))]
+ floating: Option<bool>,
+ },
+ ConvertConfig {
+ old_config_file: PathBuf,
+ },
+ ConvertLayout {
+ old_layout_file: PathBuf,
+ },
+ ConvertTheme {
+ old_theme_file: PathBuf,
+ },
+}
+
+#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
+pub enum CliAction {
+ /// Write bytes to the terminal.
+ Write { bytes: Vec<u8> },
+ /// 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<Direction>,
+ #[clap(short, long, value_parser)]
+ command: Option<String>,
+ #[clap(long, value_parser)]
+ cwd: Option<PathBuf>,
+ #[clap(short, long, value_parser, default_missing_value("true"))]
+ floating: Option<bool>,
+ },
+ /// 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<Direction>,
+ #[clap(short, long, value_parser)]
+ line_number: Option<usize>,
+ #[clap(short, long, value_parser, default_missing_value("true"))]
+ floating: Option<bool>,
+ },
+ /// 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,
+ /// Create a new tab, optionally with a specified tab layout and name
+ NewTab {
+ #[clap(short, long, value_parser)]
+ layout: Option<PathBuf>,
+ #[clap(short, long, value_parser)]
+ name: Option<String>,
+ },
}
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index b2897292..605cf898 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -1,4 +1,5 @@
use crate::input::actions::Action;
+use crate::input::config::ConversionError;
use clap::ArgEnum;
use serde::{Deserialize, Serialize};
use std::fmt;
@@ -58,6 +59,95 @@ pub enum Key {
Esc,
}
+impl FromStr for Key {
+ type Err = Box<dyn std::error::Error>;
+ fn from_str(key_str: &str) -> Result<Self, Self::Err> {
+ let mut modifier: Option<&str> = None;
+ let mut main_key: Option<&str> = None;
+ for (index, part) in key_str.split_ascii_whitespace().enumerate() {
+ 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(Direction::Left))),
+ "Right" => Ok(Key::Alt(CharOrArrow::Direction(Direction::Right))),
+ "Up" => Ok(Key::Alt(CharOrArrow::Direction(Direction::Up))),
+ "Down" => Ok(Key::Alt(CharOrArrow::Direction(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()),
+ }
+ }
+}
+
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -154,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)]
+#[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
@@ -202,6 +305,27 @@ pub enum InputMode {
Tmux,
}
+// impl TryFrom<&str> for InputMode {
+// type Error = String;
+// fn try_from(mode: &str) -> Result<Self, String> {
+// 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 {
InputMode::Normal
@@ -231,25 +355,25 @@ impl Default for PaletteColor {
}
impl FromStr for InputMode {
- type Err = Box<dyn std::error::Error>;
+ type Err = ConversionError;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
+ fn from_str(s: &str) -> Result<Self, ConversionError> {
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),
- "search" => Ok(InputMode::Search),
- "entersearch" => Ok(InputMode::EnterSearch),
- "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),
+ "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(ConversionError::UnknownInputMode(e.into())),
}
}
}
@@ -343,7 +467,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 9e457b03..3596e94e 100644
--- a/zellij-utils/src/envs.rs
+++ b/zellij-utils/src/envs.rs
@@ -2,10 +2,12 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::{
- collections::HashMap,
+ collections::{BTreeMap, HashMap},
env::{set_var, var},
};
+use std::fmt;
+
pub const ZELLIJ_ENV_KEY: &str = "ZELLIJ";
pub fn get_zellij() -> Result<String> {
Ok(var(ZELLIJ_ENV_KEY)?)
@@ -34,19 +36,31 @@ pub fn get_socket_dir() -> Result<String> {
}
/// Manage ENVIRONMENT VARIABLES from the configuration and the layout files
-#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
-pub struct EnvironmentVariablesFromYaml {
+#[derive(Default, Clone, PartialEq, Serialize, Deserialize)]
+pub struct EnvironmentVariables {
env: HashMap<String, String>,
}
-impl EnvironmentVariablesFromYaml {
+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 {
let mut env = self.clone();
env.env.extend(other.env);
env
}
-
+ pub fn from_data(data: HashMap<String, String>) -> Self {
+ EnvironmentVariables { env: data }
+ }
/// 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/errors.rs b/zellij-utils/src/errors.rs
index dac249fc..ec9d4de3 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -214,6 +214,8 @@ pub enum ScreenContext {
NewPane,
OpenInPlaceEditor,
ToggleFloatingPanes,
+ ShowFloatingPanes,
+ HideFloatingPanes,
TogglePaneEmbedOrFloating,
HorizontalSplit,
VerticalSplit,
@@ -276,6 +278,7 @@ pub enum ScreenContext {
TerminalForegroundColor,
TerminalColorRegisters,
ChangeMode,
+ ChangeModeForAllClients,
LeftClick,
RightClick,
MiddleClick,
@@ -339,6 +342,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 754151a1..8dacdc65 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -1,21 +1,42 @@
//! Definition of the actions that can be bound to keys.
use super::command::RunCommandAction;
-use super::layout::TabLayout;
+use super::layout::{Layout, PaneLayout};
+use crate::cli::CliAction;
use crate::data::InputMode;
+use crate::input::config::{ConfigError, KdlError};
use crate::input::options::OnForceClose;
+use miette::{NamedSource, Report};
use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+use std::str::FromStr;
+
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,
Up,
Down,
}
+impl FromStr for Direction {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "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
+ )),
+ }
+ }
+}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum ResizeDirection {
@@ -26,6 +47,23 @@ pub enum ResizeDirection {
Increase,
Decrease,
}
+impl FromStr for ResizeDirection {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "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
+ )),
+ }
+ }
+}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum SearchDirection {
@@ -33,6 +71,20 @@ pub enum SearchDirection {
Up,
}
+impl FromStr for SearchDirection {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ 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,
@@ -40,6 +92,54 @@ pub enum SearchOption {
Wrap,
}
+impl FromStr for SearchOption {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ 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
+ )),
+ }
+ }
+}
+
+fn split_escaped_whitespace(s: &str) -> Vec<String> {
+ 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) -> (PathBuf, Vec<String>) {
+ 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());
+ (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
@@ -55,6 +155,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.
@@ -97,6 +199,12 @@ 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<Direction>),
+ /// Open the file in a new pane using the default editor
+ EditFile(PathBuf, Option<usize>, Option<Direction>, Option<bool>), // usize is an optional line number, bool is floating true/false
+ /// Open a new floating pane
+ NewFloatingPane(Option<RunCommandAction>),
+ /// Open a new tiled (embedded, non-floating) pane
+ NewTiledPane(Option<Direction>, Option<RunCommandAction>),
/// 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
@@ -106,7 +214,7 @@ pub enum Action {
PaneNameInput(Vec<u8>),
UndoRenamePane,
/// Create a new tab, optionally with a specified tab layout.
- NewTab(Option<TabLayout>),
+ NewTab(Option<PaneLayout>, Option<String>), // the String is the tab name
/// Do nothing.
NoOp,
/// Go to the next tab.
@@ -147,6 +255,144 @@ pub enum Action {
SearchToggleOption(SearchOption),
}
+impl Action {
+ pub fn actions_from_cli(cli_action: CliAction) -> Result<Vec<Action>, String> {
+ match cli_action {
+ 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,
+ floating,
+ } => match command {
+ Some(command) => {
+ let (command, args) = split_command_and_args(command);
+ 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::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::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]),
+ 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::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| {
+ 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!("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")),
+ };
+ 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"));
+ } 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<OnForceClose> for Action {
fn from(ofc: OnForceClose) -> Action {
match ofc {
@@ -155,13 +401,3 @@ impl From<OnForceClose> for Action {
}
}
}
-
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct ActionsFromYaml(Vec<Action>);
-
-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 6d394636..ca161c94 100644
--- a/zellij-utils/src/input/config.rs
+++ b/zellij-utils/src/input/config.rs
@@ -1,98 +1,135 @@
-//! Deserializes configuration options.
-use std::fmt;
+use crate::data::Palette;
+use miette::{Diagnostic, LabeledSpan, NamedSource, SourceCode};
use std::fs::File;
use std::io::{self, Read};
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use thiserror::Error;
-use serde::{Deserialize, Serialize};
-use std::convert::{TryFrom, TryInto};
+use std::convert::TryFrom;
-use super::keybinds::{Keybinds, KeybindsFromYaml};
+use super::keybinds::Keybinds;
use super::options::Options;
-use super::plugins::{PluginsConfig, PluginsConfigError, PluginsConfigFromYaml};
-use super::theme::{ThemesFromYamlIntermediate, UiConfigFromYaml};
+use super::plugins::{PluginsConfig, PluginsConfigError};
+use super::theme::{Themes, UiConfig};
use crate::cli::{CliArgs, Command};
-use crate::envs::EnvironmentVariablesFromYaml;
+use crate::envs::EnvironmentVariables;
use crate::setup;
-const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml";
+const DEFAULT_CONFIG_FILE_NAME: &str = "config.kdl";
type ConfigResult = Result<Config, ConfigError>;
-/// Intermediate deserialization config struct
-#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq)]
-pub struct ConfigFromYaml {
- #[serde(flatten)]
- pub options: Option<Options>,
- pub keybinds: Option<KeybindsFromYaml>,
- pub themes: Option<ThemesFromYamlIntermediate>,
- #[serde(flatten)]
- pub env: Option<EnvironmentVariablesFromYaml>,
- #[serde(default)]
- pub plugins: PluginsConfigFromYaml,
- pub ui: Option<UiConfigFromYaml>,
-}
-
/// Main configuration.
-#[derive(Debug, Clone, PartialEq, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Default)]
pub struct Config {
pub keybinds: Keybinds,
pub options: Options,
- pub themes: Option<ThemesFromYamlIntermediate>,
+ pub themes: Themes,
pub plugins: PluginsConfig,
- pub ui: Option<UiConfigFromYaml>,
- pub env: EnvironmentVariablesFromYaml,
+ pub ui: UiConfig,
+ pub env: EnvironmentVariables,
}
#[derive(Error, Debug)]
+pub struct KdlError {
+ pub error_message: String,
+ pub src: Option<NamedSource>,
+ pub offset: Option<usize>,
+ pub len: Option<usize>,
+}
+
+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<Box<dyn Display + 'a>> {
+ // 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<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
+ 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}")]
- Serde(#[from] serde_yaml::Error),
+ KdlDeserializationError(#[from] kdl::KdlError),
+ #[error("KdlDeserialization error: {0}")]
+ KdlError(KdlError), // TODO: consolidate these
// Io error
#[error("IoError: {0}")]
Io(#[from] io::Error),
+ #[error("Config error: {0}")]
+ Std(#[from] Box<dyn std::error::Error>),
// Io error with path context
#[error("IoError: {0}, File: {1}")]
IoPath(io::Error, PathBuf),
// 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),
+ #[error("{0}")]
+ ConversionError(#[from] ConversionError),
}
-impl Default for Config {
- fn default() -> Self {
- let keybinds = Keybinds::default();
- let options = Options::default();
- let themes = None;
- let env = EnvironmentVariablesFromYaml::default();
- let plugins = PluginsConfig::default();
- let ui = None;
-
- Config {
- keybinds,
- options,
- themes,
- plugins,
- env,
- ui,
- }
+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}")]
+ UnknownInputMode(String),
+}
+
impl TryFrom<&CliArgs> for Config {
type Error = ConfigError;
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));
}
if let Some(Command::Setup(ref setup)) = opts.command {
@@ -109,7 +146,8 @@ 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))
} else {
Config::from_default_assets()
}
@@ -120,126 +158,84 @@ impl TryFrom<&CliArgs> for Config {
}
impl Config {
- /// Uses defaults, but lets config override them.
- pub fn from_yaml(yaml_config: &str) -> ConfigResult {
- let maybe_config_from_yaml: Option<ConfigFromYaml> = 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(),
+ pub fn theme_config(&self, opts: &Options) -> Option<Palette> {
+ 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),
}
}
-
- /// Deserializes from given path.
- pub fn new(path: &Path) -> ConfigResult {
+ /// Gets default configuration from assets
+ pub fn from_default_assets() -> ConfigResult {
+ 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),
+ }
+ }
+ pub fn from_path(path: &PathBuf, default_config: Option<Config>) -> ConfigResult {
match File::open(path) {
Ok(mut file) => {
- let mut yaml_config = String::new();
- file.read_to_string(&mut yaml_config)
+ 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_yaml(&yaml_config)?)
+ match Config::from_kdl(&kdl_config, default_config) {
+ Ok(config) => Ok(config),
+ Err(ConfigError::KdlDeserializationError(kdl_error)) => {
+ let error_message = match kdl_error.kind {
+ kdl::KdlErrorKind::Context("valid node terminator") => {
+ 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"))
+ },
+ };
+ 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())),
}
}
-
- /// 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)
- }
-
- /// 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<ConfigFromYaml> 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,
- })
- }
-}
-
-// 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 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 std::io::Write;
-
use tempfile::tempdir;
- use super::*;
-
#[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 +248,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 +280,387 @@ 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());
+ }
+
+ #[test]
+ fn can_define_options_in_configfile() {
+ 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();
+ 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]
+ 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()
+ },
+ },
+ );
+ let expected_themes = Themes::from_data(expected_themes);
+ assert_eq!(config.themes, 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()
+ },
+ },
+ );
+ let expected_themes = Themes::from_data(expected_themes);
+ assert_eq!(config.themes, 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()
+ },
+ },
+ );
+ let expected_themes = Themes::from_data(expected_themes);
+ assert_eq!(config.themes, 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, 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 6e6f2ab9..eaed03fd 100644
--- a/zellij-utils/src/input/keybinds.rs
+++ b/zellij-utils/src/input/keybinds.rs
@@ -1,325 +1,69 @@
-//! Mapping of inputs to sequences of actions.
use std::collections::{BTreeMap, HashMap};
use super::actions::Action;
-use super::config;
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)]
-pub struct Keybinds(HashMap<InputMode, ModeKeybinds>);
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct ModeKeybinds(BTreeMap<Key, Vec<Action>>);
+#[derive(Clone, PartialEq, Deserialize, Serialize, Default)]
+pub struct Keybinds(pub HashMap<InputMode, HashMap<Key, Vec<Action>>>);
-/// Intermediate struct used for deserialisation
-/// Used in the config file.
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct KeybindsFromYaml {
- #[serde(flatten)]
- keybinds: HashMap<InputMode, Vec<KeyActionUnbind>>,
- #[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<KeyActionFromYaml>,
- unbind: Unbind,
-}
-
-/// Intermediate struct used for deserialisation
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct KeyActionFromYaml {
- action: Vec<Action>,
- key: Vec<Key>,
-}
-
-/// 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<Key>),
- // 2. All
- All(bool),
-}
-
-impl Default for Keybinds {
- // Use once per codepath
- // TODO investigate why
- fn default() -> Keybinds {
- Self::from_default_assets()
+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)
}
}
impl Keybinds {
- pub fn new() -> Keybinds {
- Keybinds(HashMap::<InputMode, ModeKeybinds>::new())
- }
-
- fn from_default_assets() -> Keybinds {
- config::Config::from_default_assets()
- .expect("Keybinds from default assets Error!")
- .keybinds
- }
-
- pub fn to_keybinds_vec(&self) -> KeybindsVec {
- let mut ret = vec![];
- for (mode, mode_binds) in &self.0 {
- ret.push((*mode, mode_binds.to_cloned_vec()));
- }
- ret
- }
-
- pub fn get_mode_keybinds(&self, mode: &InputMode) -> &ModeKeybinds {
+ pub fn get_actions_for_key_in_mode(&self, mode: &InputMode, key: &Key) -> Option<&Vec<Action>> {
self.0
.get(mode)
- .expect("Failed to get Keybinds for current mode")
- }
-
- /// Entrypoint from the config module
- pub fn get_default_keybinds_with_config(from_yaml: Option<KeybindsFromYaml>) -> 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<InputMode, Unbind> = 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<InputMode, Unbind> = 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)
+ .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key))
}
-
- /// Unbind [`Key`] bindings respective to their mode
- fn unbind_mode(&self, unbind: HashMap<InputMode, Unbind>) -> 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());
- }
- }
- keybinds
- }
-
- /// 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);
- }
- }
- keybinds
- }
-
- /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current
- /// [`InputMode`] and [`Keybinds`].
- pub fn key_to_actions(
+ pub fn get_actions_for_key_in_mode_or_default_action(
+ &self,
+ mode: &InputMode,
key: &Key,
raw_bytes: Vec<u8>,
- mode: &InputMode,
- keybinds: &Keybinds,
) -> Vec<Action> {
- 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])
- };
- 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)),
- InputMode::EnterSearch => mode_keybind_or_action(Action::SearchInput(raw_bytes)),
- _ => mode_keybind_or_action(Action::NoOp),
- }
- }
-}
-
-impl ModeKeybinds {
- fn new() -> ModeKeybinds {
- ModeKeybinds(BTreeMap::<Key, Vec<Action>>::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<Key>) -> Self {
- let mut keymap = self;
- for key in unbind {
- keymap.0.remove(&key);
- }
- keymap
- }
-
- pub fn to_cloned_vec(&self) -> Vec<(Key, Vec<Action>)> {
self.0
- .iter()
- .map(|(key, vac)| (*key, vac.clone()))
- .collect()
- }
-}
-
-impl From<KeybindsFromYaml> 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
+ .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)])
}
-}
-
-/// For each [`Key`] assigned to [`Action`]s,
-/// map the [`Action`]s to the [`Key`]
-impl From<KeyActionFromYaml> 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::<BTreeMap<Key, Vec<Action>>>(),
- )
+ pub fn get_input_mode_mut(&mut self, input_mode: &InputMode) -> &mut HashMap<Key, Vec<Action>> {
+ self.0.entry(*input_mode).or_insert_with(HashMap::new)
}
-}
-
-impl From<KeyActionUnbind> 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(),
+ pub fn default_action_for_mode(&self, mode: &InputMode, raw_bytes: Vec<u8>) -> Action {
+ match *mode {
+ InputMode::Normal | InputMode::Locked => Action::Write(raw_bytes),
+ InputMode::RenameTab => Action::TabNameInput(raw_bytes),
+ InputMode::RenamePane => Action::PaneNameInput(raw_bytes),
+ InputMode::EnterSearch => Action::SearchInput(raw_bytes),
+ _ => Action::NoOp,
}
}
-}
-
-impl From<Vec<KeyActionFromYaml>> for ModeKeybinds {
- fn from(key_action_from_yaml: Vec<KeyActionFromYaml>) -> 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());
+ pub fn to_keybinds_vec(&self) -> KeybindsVec {
+ let mut ret = vec![];
+ for (mode, mode_binds) in &self.0 {
+ let mut mode_binds_vec: Vec<(Key, Vec<Action>)> = vec![];
+ for (key, actions) in mode_binds {
+ mode_binds_vec.push((key.clone(), actions.clone()));
}
+ ret.push((*mode, mode_binds_vec))
}
- mode_keybinds
- }
-}
-
-impl Default for Unbind {
- fn default() -> Unbind {
- Unbind::All(false)
+ ret
}
}
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 3f0b3929..eea80859 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -11,22 +11,20 @@
use crate::{
input::{
command::RunCommand,
- config::{ConfigError, LayoutNameInTabError},
+ config::{Config, ConfigError},
},
pane_size::{Dimension, PaneGeom},
setup,
};
-use super::{
- config::ConfigFromYaml,
- plugins::{PluginTag, PluginsConfigError},
-};
+use std::str::FromStr;
+
+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},
};
@@ -34,20 +32,18 @@ use std::{fs::File, io::prelude::*};
use url::Url;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
-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,
}
}
}
@@ -55,7 +51,7 @@ impl Not for Direction {
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum SplitSize {
#[serde(alias = "percent")]
- Percent(u64), // 1 to 100
+ Percent(usize), // 1 to 100
#[serde(alias = "fixed")]
Fixed(usize), // An absolute number of columns or rows
}
@@ -69,21 +65,6 @@ pub enum Run {
}
#[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,
@@ -123,487 +104,292 @@ 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, Eq, Default)]
pub struct Layout {
- pub direction: Direction,
- #[serde(default)]
- pub pane_name: Option<String>,
- #[serde(default)]
- pub parts: Vec<Layout>,
+ pub tabs: Vec<(Option<String>, PaneLayout)>,
+ pub focused_tab_index: Option<usize>,
+ pub template: Option<PaneLayout>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
+pub struct PaneLayout {
+ pub children_split_direction: SplitDirection,
+ pub name: Option<String>,
+ pub children: Vec<PaneLayout>,
pub split_size: Option<SplitSize>,
pub run: Option<Run>,
- #[serde(default)]
pub borderless: bool,
pub focus: Option<bool>,
+ pub external_children_index: Option<usize>,
}
-// 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<TabLayout>,
- #[serde(default)]
- pub session: SessionFromYaml,
- #[serde(flatten)]
- pub config: Option<ConfigFromYaml>,
-}
-
-// 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<TabLayout>,
-}
-
-type LayoutFromYamlIntermediateResult = Result<LayoutFromYamlIntermediate, ConfigError>;
-
-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<LayoutFromYamlIntermediate> = 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));
+impl PaneLayout {
+ pub fn insert_children_layout(
+ &mut self,
+ children_layout: &mut PaneLayout,
+ ) -> Result<bool, ConfigError> {
+ // 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)
},
- Ok(config) => config,
- };
-
- match layout {
- Some(layout) => {
- for tab in layout.tabs.clone() {
- tab.check()?;
+ None => {
+ for pane in self.children.iter_mut() {
+ if pane.insert_children_layout(children_layout)? {
+ return Ok(true);
+ }
}
- Ok(layout)
+ Ok(false)
},
- None => Ok(LayoutFromYamlIntermediate::default()),
}
}
+ 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
+ }
+ pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(PaneLayout, PaneGeom)> {
+ split_space(space, self, space)
+ }
+ pub fn extract_run_instructions(&self) -> Vec<Option<Run>> {
+ 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 {
+ let mut default_layout = PaneLayout::default();
+ default_layout.children = vec![PaneLayout::default()];
+ default_layout
+ }
+}
- 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));
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub enum LayoutParts {
+ Tabs(Vec<(Option<String>, Layout)>), // String is the tab name
+ Panes(Vec<Layout>),
+}
+
+impl LayoutParts {
+ pub fn is_empty(&self) -> bool {
+ match self {
+ LayoutParts::Panes(panes) => panes.is_empty(),
+ LayoutParts::Tabs(tabs) => tabs.is_empty(),
+ }
+ }
+ pub fn insert_pane(&mut self, index: usize, layout: Layout) -> Result<(), ConfigError> {
+ match self {
+ LayoutParts::Panes(panes) => {
+ panes.insert(index, layout);
+ Ok(())
},
- Ok(config) => config,
- };
- Ok(layout)
+ LayoutParts::Tabs(_tabs) => Err(ConfigError::new_kdl_error(
+ "Trying to insert a pane into a tab layout".into(),
+ 0,
+ 0,
+ )),
+ }
}
+}
- pub fn to_layout_and_config(&self) -> (LayoutFromYaml, Option<ConfigFromYaml>) {
- let config = self.config.clone();
- let layout = self.clone().into();
- (layout, config)
+impl Default for LayoutParts {
+ fn default() -> Self {
+ LayoutParts::Panes(vec![])
}
+}
- pub fn from_path_or_default(
- layout: Option<&PathBuf>,
+impl Layout {
+ pub fn stringified_from_path_or_default(
+ layout_path: Option<&PathBuf>,
layout_dir: Option<PathBuf>,
- ) -> Option<LayoutFromYamlIntermediateResult> {
- layout
- .map(|layout| {
+ ) -> 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
// 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 {
+ if layout_path.extension().is_some() || layout_path.components().count() > 1 {
// We look localy!
- LayoutFromYamlIntermediate::from_path(layout)
+ Layout::stringified_from_path(layout_path)
} else {
// We look in the default dir
- LayoutFromYamlIntermediate::from_dir(layout, layout_dir.as_ref())
+ Layout::stringified_from_dir(layout_path, layout_dir.as_ref())
}
- })
- .or_else(|| {
- Some(LayoutFromYamlIntermediate::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(),
+ ),
+ }
}
-
- // It wants to use Path here, but that doesn't compile.
- #[allow(clippy::ptr_arg)]
- pub fn from_dir(
+ pub fn from_path_or_default(
+ layout_path: Option<&PathBuf>,
+ layout_dir: Option<PathBuf>,
+ 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))
+ }
+ pub fn from_str(raw: &str, path_to_raw_layout: String) -> Result<Layout, ConfigError> {
+ Layout::from_kdl(raw, path_to_raw_layout)
+ }
+ pub fn stringified_from_dir(
layout: &PathBuf,
layout_dir: Option<&PathBuf>,
- ) -> LayoutFromYamlIntermediateResult {
+ ) -> Result<(String, String), ConfigError> {
+ // (path_to_layout as String, stringified_layout)
match layout_dir {
Some(dir) => {
let layout_path = &dir.join(layout);
- if layout_path.with_extension("yaml").exists() {
- Self::from_path(layout_path)
+ if layout_path.with_extension("kdl").exists() {
+ Self::stringified_from_path(layout_path)
} else {
- LayoutFromYamlIntermediate::from_default_assets(layout)
+ Layout::stringified_from_default_assets(layout)
}
},
- None => LayoutFromYamlIntermediate::from_default_assets(layout),
- }
- }
- // Currently still needed but on nightly
- // this is already possible:
- // HashMap<&'static str, Vec<u8>>
- 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(),
- )),
+ None => Layout::stringified_from_default_assets(layout),
}
}
-
- // 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<LayoutFromYaml, ConfigError>;
-
-impl LayoutFromYaml {
- pub fn new(layout_path: &Path) -> LayoutFromYamlResult {
+ 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("yaml")))
+ .or_else(|_| File::open(&layout_path.with_extension("kdl")))
.map_err(|e| ConfigError::IoPath(e, layout_path.into()))?;
- let mut layout = String::new();
- layout_file.read_to_string(&mut layout)?;
- let layout: Option<LayoutFromYaml> = 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<PathBuf>,
- ) -> Option<LayoutFromYamlResult> {
- 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(),
- ))
- })
+ let mut kdl_layout = String::new();
+ layout_file.read_to_string(&mut kdl_layout)?;
+ Ok((layout_path.as_os_str().to_string_lossy().into(), kdl_layout))
}
-
- // Currently still needed but on nightly
- // this is already possible:
- // HashMap<&'static str, Vec<u8>>
- pub fn from_default_assets(path: &Path) -> LayoutFromYamlResult {
+ 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::default_from_assets(),
- Some("strider") => Self::strider_from_assets(),
- Some("disable-status-bar") => Self::disable_status_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(),
)),
}
}
-
- // 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 stringified_default_from_assets() -> Result<String, ConfigError> {
+ Ok(String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?)
}
- 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 stringified_strider_from_assets() -> Result<String, ConfigError> {
+ Ok(String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?)
}
- 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)
+ pub fn stringified_disable_status_from_assets() -> Result<String, ConfigError> {
+ Ok(String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)
}
-}
-
-// 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<String>,
- #[serde(default = "default_as_some_true")]
- pub attach: Option<bool>,
-}
-
-fn default_as_some_true() -> Option<bool> {
- 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<String>,
- #[serde(default)]
- pub borderless: bool,
- #[serde(default)]
- pub parts: Vec<LayoutTemplate>,
- #[serde(default)]
- pub body: bool,
- pub split_size: Option<SplitSize>,
- pub focus: Option<bool>,
- pub run: Option<RunFromYaml>,
-}
-impl LayoutTemplate {
- // Insert an optional `[TabLayout]` at the correct position
- pub fn insert_tab_layout(mut self, tab_layout: Option<TabLayout>) -> 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
+ pub fn stringified_compact_from_assets() -> Result<String, ConfigError> {
+ Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)
}
- fn from_vec_tab_layout(tab_layout: Vec<TabLayout>) -> Vec<Self> {
- 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<String>,
- #[serde(default)]
- pub borderless: bool,
- #[serde(default)]
- pub parts: Vec<TabLayout>,
- pub split_size: Option<SplitSize>,
- #[serde(default)]
- pub name: String,
- pub focus: Option<bool>,
- pub run: Option<RunFromYaml>,
-}
-
-impl TabLayout {
- fn check(&self) -> Result<TabLayout, ConfigError> {
- for part in &self.parts {
- part.check()?;
- if !part.name.is_empty() {
- return Err(ConfigError::LayoutNameInTab(LayoutNameInTabError));
- }
+ pub fn new_tab(&self) -> PaneLayout {
+ match &self.template {
+ Some(template) => template.clone(),
+ None => PaneLayout::default(),
}
- 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 is_empty(&self) -> bool {
+ !self.tabs.is_empty()
}
-
- 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<Option<Run>> {
- 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<Layout>) {
- self.parts.append(&mut parts);
+ // TODO: do we need both of these?
+ pub fn has_tabs(&self) -> bool {
+ !self.tabs.is_empty()
}
- fn from_vec_tab_layout(tab_layout: Vec<TabLayout>) -> Result<Vec<Self>, ConfigError> {
- tab_layout
- .iter()
- .map(|tab_layout| Layout::try_from(tab_layout.to_owned()))
- .collect()
+ pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout)> {
+ // String is the tab name
+ self.tabs.clone()
}
- fn from_vec_template_layout(
- layout_template: Vec<LayoutTemplate>,
- ) -> Result<Vec<Self>, ConfigError> {
- layout_template
- .iter()
- .map(|layout_template| Layout::try_from(layout_template.to_owned()))
- .collect()
+ pub fn focused_tab_index(&self) -> Option<usize> {
+ self.focused_tab_index
}
}
-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)> {
+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<Option<SplitSize>> = layout.parts.iter().map(|part| part.split_size).collect();
+ let sizes: Vec<Option<SplitSize>> =
+ 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) =
- 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 (
+ 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();
- for (&size, part) in sizes.iter().zip(&layout.parts) {
- let split_dimension = match size {
+ 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 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
- }
+ .map(|&s| match s {
+ Some(SplitSize::Percent(ip)) => ip as f64,
+ _ => 0.0,
})
.sum::<f64>()
} else {
@@ -612,22 +398,17 @@ fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneG
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 {
+ 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,
},
- Direction::Horizontal => PaneGeom {
+ SplitDirection::Horizontal => PaneGeom {
x: space_to_split.x,
y: current_position,
cols: inherited_dimension,
@@ -635,18 +416,32 @@ 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 layout.parts.iter().enumerate() {
+ // 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),
+ }
+ }
+ }
+ for (i, part) in layout.children.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);
+ 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
}
@@ -658,163 +453,45 @@ impl TryFrom<Url> 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)),
}
}
}
-impl TryFrom<RunFromYaml> for Run {
- type Error = PluginsConfigError;
-
- fn try_from(run: RunFromYaml) -> Result<Self, Self::Error> {
- 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<LayoutFromYamlIntermediate> 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<LayoutFromYaml> 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 {
+impl Default for SplitDirection {
fn default() -> Self {
- LayoutFromYaml::default().into()
- }
-}
-
-impl TryFrom<TabLayout> for Layout {
- type Error = ConfigError;
-
- fn try_from(tab: TabLayout) -> Result<Self, Self::Error> {
- 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()?,
- })
+ SplitDirection::Horizontal
}
}
-impl From<TabLayout> 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 FromStr for SplitDirection {
+ type Err = Box<dyn std::error::Error>;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "vertical" | "Vertical" => Ok(SplitDirection::Vertical),
+ "horizontal" | "Horizontal" => Ok(SplitDirection::Horizontal),
+ _ => Err("split direction must be either vertical or horizontal".into()),
}
}
}
-impl TryFrom<LayoutTemplate> for Layout {
- type Error = ConfigError;
-
- fn try_from(template: LayoutTemplate) -> Result<Self, Self::Error> {
- 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 FromStr for SplitSize {
+ type Err = Box<dyn std::error::Error>;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.chars().last() == Some('%') {
+ let char_count = s.chars().count();
+ 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)?;
+ Ok(SplitSize::Fixed(fixed_size))
}
}
}
-impl Default for Direction {
- fn default() -> Self {
- Direction::Horizontal
- }
-}
-
// 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 29a7816c..6722cfd8 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 460712ef..60707597 100644
--- a/zellij-utils/src/input/options.rs
+++ b/zellij-utils/src/input/options.rs
@@ -106,6 +106,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<PathBuf>,
+
+ #[clap(long, value_parser)]
+ #[serde(default)]
+ pub session_name: Option<String>,
+
+ #[clap(long, value_parser)]
+ #[serde(default)]
+ pub attach_to_session: Option<bool>,
}
#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
@@ -122,6 +130,17 @@ impl Default for Clipboard {
}
}
+impl FromStr for Clipboard {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "System" | "system" => Ok(Self::System),
+ "Primary" | "primary" => Ok(Self::Primary),
+ _ => Err(format!("No such clipboard: {}", s)),
+ }
+ }
+}
+
impl Options {
pub fn from_yaml(from_yaml: Option<Options>) -> Options {
if let Some(opts) = from_yaml {
@@ -130,7 +149,6 @@ impl Options {
Options::default()
}
}
-
/// Merges two [`Options`] structs, a `Some` in `other`
/// will supersede a `Some` in `self`
// TODO: Maybe a good candidate for a macro?
@@ -153,6 +171,10 @@ 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,
@@ -171,6 +193,8 @@ impl Options {
copy_clipboard,
copy_on_select,
scrollback_editor,
+ session_name,
+ attach_to_session,
}
}
@@ -208,6 +232,10 @@ 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,
@@ -226,6 +254,8 @@ impl Options {
copy_clipboard,
copy_on_select,
scrollback_editor,
+ session_name,
+ attach_to_session,
}
}
@@ -249,7 +279,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<CliOptions> for Options {
@@ -280,6 +310,7 @@ impl From<CliOptions> 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/plugins.rs b/zellij-utils/src/input/plugins.rs
index 8dec828d..55ea4283 100644
--- a/zellij-utils/src/input/plugins.rs
+++ b/zellij-utils/src/input/plugins.rs
@@ -1,45 +1,39 @@
//! 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 lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use url::Url;
-use super::config::ConfigFromYaml;
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()
- };
-}
-
-#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
-pub struct PluginsConfigFromYaml(Vec<PluginConfigFromYaml>);
+use std::collections::BTreeMap;
+use std::fmt;
/// Used in the config struct for plugin metadata
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct PluginsConfig(HashMap<PluginTag, PluginConfig>);
+#[derive(Clone, PartialEq, Deserialize, Serialize)]
+pub struct PluginsConfig(pub HashMap<PluginTag, PluginConfig>);
+
+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 {
Self(HashMap::new())
}
-
- /// 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 from_data(data: HashMap<PluginTag, PluginConfig>) -> Self {
+ PluginsConfig(data)
}
/// Get plugin config from run configuration specified in layout files.
@@ -74,37 +68,7 @@ impl PluginsConfig {
impl Default for PluginsConfig {
fn default() -> Self {
- Self::get_plugins_with_default(PluginsConfig::new())
- }
-}
-
-impl TryFrom<PluginsConfigFromYaml> for PluginsConfig {
- type Error = PluginsConfigError;
-
- fn try_from(yaml: PluginsConfigFromYaml) -> Result<Self, PluginsConfigError> {
- 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<PluginConfigFromYaml> 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),
- }
+ PluginsConfig(HashMap::new())
}
}
@@ -172,31 +136,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()))]
@@ -206,99 +145,3 @@ pub enum PluginsConfigError {
#[error("Could not find plugin at the path: '{0:?}'")]
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(())
- }
-}
diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs
index b148e0d4..c76deb31 100644
--- a/zellij-utils/src/input/theme.rs
+++ b/zellij-utils/src/input/theme.rs
@@ -2,72 +2,75 @@ use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
+use std::{
+ collections::{BTreeMap, HashMap},
+ fmt,
+};
-use std::fs::File;
-use std::io::Read;
-use std::path::Path;
-use std::{collections::HashMap, fmt};
-
-use super::{config::ConfigError, options::Options};
-use crate::data::{Palette, PaletteColor};
-use crate::shared::detect_theme_hue;
-
-/// Intermediate deserialization of themes
-#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
-pub struct ThemesFromYamlIntermediate(HashMap<String, Theme>);
+use crate::data::Palette;
-#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
-pub struct ThemesFromYaml {
- pub themes: ThemesFromYamlIntermediate,
+#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
+pub struct UiConfig {
+ pub pane_frames: FrameConfig,
}
-type ThemesFromYamlResult = Result<ThemesFromYaml, ConfigError>;
-
-#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
-pub struct UiConfigFromYaml {
- pub pane_frames: FrameConfigFromYaml,
+impl UiConfig {
+ pub fn merge(&self, other: UiConfig) -> Self {
+ let mut merged = self.clone();
+ merged.pane_frames = merged.pane_frames.merge(other.pane_frames);
+ merged
+ }
}
#[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 ThemeFromYaml {
- palette: PaletteFromYaml,
+impl FrameConfig {
+ pub fn merge(&self, other: FrameConfig) -> Self {
+ let mut merged = self.clone();
+ merged.rounded_corners = other.rounded_corners;
+ merged
+ }
}
-#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
-pub struct Theme {
- #[serde(flatten)]
- palette: PaletteFromYaml,
+#[derive(Clone, PartialEq, Default)]
+pub struct Themes(HashMap<String, Theme>);
+
+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)
+ }
}
-/// 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,
+impl Themes {
+ pub fn from_data(theme_data: HashMap<String, Theme>) -> 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() {
+ merged.0.insert(name, theme);
+ }
+ merged
+ }
+ pub fn get_theme(&self, theme_name: &str) -> Option<&Theme> {
+ self.0.get(theme_name)
+ }
}
-/// 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(Debug, Clone, PartialEq, Deserialize, Serialize)]
+pub struct Theme {
+ #[serde(flatten)]
+ pub palette: Palette,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -134,96 +137,6 @@ impl Serialize for HexColor {
}
}
-impl Default for PaletteColorFromYaml {
- fn default() -> Self {
- PaletteColorFromYaml::EightBit(0)
- }
-}
-
-impl ThemesFromYaml {
- pub fn from_path(theme_path: &Path) -> ThemesFromYamlResult {
- let mut theme_file = File::open(&theme_path)
- .or_else(|_| File::open(&theme_path.with_extension("yaml")))
- .map_err(|e| ConfigError::IoPath(e, theme_path.into()))?;
-
- let mut theme = String::new();
- theme_file.read_to_string(&mut theme)?;
-
- let theme: ThemesFromYaml = match serde_yaml::from_str(&theme) {
- Err(e) => return Err(ConfigError::Serde(e)),
- Ok(theme) => theme,
- };
-
- Ok(theme)
- }
-}
-
-impl From<ThemesFromYaml> for ThemesFromYamlIntermediate {
- fn from(yaml: ThemesFromYaml) -> Self {
- yaml.themes
- }
-}
-
-impl ThemesFromYamlIntermediate {
- pub fn theme_config(self, opts: &Options) -> Option<Palette> {
- 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<Theme> {
- self.0.remove(&theme)
- }
-
- #[allow(clippy::wrong_self_convention)]
- fn from_default_theme(&mut self, theme: String) -> Option<Palette> {
- self.clone()
- .get_theme(theme)
- .map(|t| Palette::from(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<PaletteFromYaml> 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<PaletteColorFromYaml> 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()),
- }
- }
-}
-
-// The unit test location.
#[cfg(test)]
#[path = "./unit/theme_test.rs"]
mod theme_test;
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 6b599373..00000000
--- 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 65378759..00000000
--- 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 7794c912..00000000
--- 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 8b0f3daf..00000000
--- 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 144d555b..00000000
--- 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 9ad1034d..00000000
--- 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-tabs-merged-correctly.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml
deleted file mode 100644
index e2a09a73..00000000
--- 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/fixtures/themes/dracula.kdl b/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl
new file mode 100644
index 00000000..77a25f4b
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/themes/dracula.kdl
@@ -0,0 +1,18 @@
+// 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/dracula.yaml b/zellij-utils/src/input/unit/fixtures/themes/dracula.yaml
deleted file mode 100644
index b9c8a5af..00000000
--- 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 00000000..361a21fd
--- /dev/null
+++ b/zellij-utils/src/input/unit/fixtures/themes/nord.kdl
@@ -0,0 +1,17 @@
+// 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 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 61851ad2..00000000
--- 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 1579f013..57126d59 100644
--- a/zellij-utils/src/input/unit/keybinds_test.rs
+++ b/zellij-utils/src/input/unit/keybinds_test.rs
@@ -1,810 +1,544 @@
use super::super::actions::*;
use super::super::keybinds::*;
-use crate::data::{CharOrArrow, Key};
+use crate::data::{self, CharOrArrow, Key};
+use crate::input::config::Config;
+use insta::assert_snapshot;
+use strum::IntoEnumIterator;
#[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);
-
+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!(
- keybinds_expected,
- keybinds_self.merge_keybinds(keybinds_other)
- )
+ ctrl_g_normal_mode_action,
+ Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
+ "Keybinding successfully defined in config"
+ );
}
#[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);
-
+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!(
- 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::<InputMode, Vec<KeyActionUnbind>>::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::<InputMode, Vec<KeyActionUnbind>>::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::<InputMode, Vec<KeyActionUnbind>>::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());
+ 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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 unbind_multiple_keybinds_all_modes() {
- let unbind = Unbind::Keys(vec![
- Key::Alt(CharOrArrow::Char('n')),
- Key::Alt(CharOrArrow::Char('h')),
- ]);
- let keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 unbind_single_keybind_all_modes() {
- let keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 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::<InputMode, Vec<KeyActionUnbind>>::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());
+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 22a10c6f..b4b309ba 100644
--- a/zellij-utils/src/input/unit/layout_test.rs
+++ b/zellij-utils/src/input/unit/layout_test.rs
@@ -1,859 +1,1013 @@
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";
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ template: Some(PaneLayout::default()),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[test]
-fn default_layout_is_ok() {
- let path = default_layout_dir("default.yaml".into());
- let layout = LayoutFromYaml::new(&path);
- assert!(layout.is_ok());
+fn layout_with_one_pane() {
+ let kdl_layout = r#"
+ layout {
+ pane
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ template: Some(PaneLayout {
+ children: vec![PaneLayout::default()],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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);
+fn layout_with_multiple_panes() {
+ let kdl_layout = r#"
+ layout {
+ pane
+ pane
+ pane
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ template: Some(PaneLayout {
+ children: vec![
+ PaneLayout::default(),
+ PaneLayout::default(),
+ PaneLayout::default(),
+ ],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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,
+fn layout_with_nested_panes() {
+ let kdl_layout = r#"
+ layout {
+ pane split_direction="Vertical" {
+ pane
+ pane
+ }
+ pane {
+ pane
+ pane
+ }
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ template: Some(PaneLayout {
+ children: vec![
+ PaneLayout {
+ children_split_direction: SplitDirection::Vertical,
+ children: vec![PaneLayout::default(), PaneLayout::default()],
+ ..Default::default()
+ },
+ PaneLayout {
+ children: vec![PaneLayout::default(), PaneLayout::default()],
+ ..Default::default()
+ },
+ ],
+ ..Default::default()
+ }),
+ ..Default::default()
};
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+ assert_eq!(layout, expected_layout);
}
#[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,
+fn layout_with_tabs() {
+ let kdl_layout = r#"
+ layout {
+ tab
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ tabs: vec![(None, PaneLayout::default())],
+ template: Some(PaneLayout::default()),
+ ..Default::default()
};
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+ assert_eq!(layout, expected_layout);
}
#[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());
+fn layout_with_nested_differing_tabs() {
+ let kdl_layout = r#"
+ layout {
+ tab split_direction="Vertical" {
+ pane
+ pane
+ pane
+ }
+ tab {
+ pane
+ pane
+ }
+ }
+ "#;
+ 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()
+ },
+ ),
+ ],
+ template: Some(PaneLayout::default()),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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());
+fn layout_with_panes_in_different_mixed_split_sizes() {
+ let kdl_layout = r#"
+ layout {
+ pane size=1;
+ pane size="10%";
+ pane;
+ pane size=2;
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ template: Some(PaneLayout {
+ children: vec![
+ PaneLayout {
+ split_size: Some(SplitSize::Fixed(1)),
+ ..Default::default()
+ },
+ PaneLayout {
+ split_size: Some(SplitSize::Percent(10)),
+ ..Default::default()
+ },
+ PaneLayout {
+ split_size: None,
+ ..Default::default()
+ },
+ PaneLayout {
+ split_size: Some(SplitSize::Fixed(2)),
+ ..Default::default()
+ },
+ ],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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);
+fn layout_with_command_panes() {
+ let kdl_layout = r#"
+ layout {
+ pane command="htop"
+ }
+ "#;
+ 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()
+ })),
+ ..Default::default()
+ }],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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());
+fn layout_with_command_panes_and_cwd() {
+ let kdl_layout = r#"
+ layout {
+ pane command="htop" cwd="/path/to/my/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()
+ }],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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);
+fn layout_with_command_panes_and_cwd_and_args() {
+ let kdl_layout = r#"
+ layout {
+ pane command="htop" cwd="/path/to/my/cwd" {
+ args "-h" "-v"
+ }
+ }
+ "#;
+ 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()
+ }],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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,
+fn layout_with_plugin_panes() {
+ let kdl_layout = r#"
+ layout {
+ pane {
+ plugin location="zellij:tab-bar"
+ }
+ pane {
+ plugin location="file:/path/to/my/plugin.wasm"
+ }
+ }
+ "#;
+ 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::Plugin(RunPlugin {
+ location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")),
+ _allow_exec_host_cmd: false,
+ })),
+ ..Default::default()
},
- 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,
+ PaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ location: RunPluginLocation::File(PathBuf::from("/path/to/my/plugin.wasm")),
+ _allow_exec_host_cmd: false,
+ })),
+ ..Default::default()
},
],
- split_size: None,
- run: None,
- }],
- split_size: None,
- run: None,
+ ..Default::default()
+ }),
+ ..Default::default()
};
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+ assert_eq!(layout, expected_layout);
}
#[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,
+fn layout_with_borderless_panes() {
+ let kdl_layout = r#"
+ layout {
+ pane borderless=true
+ }
+ "#;
+ 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()
+ }],
+ ..Default::default()
+ }),
+ ..Default::default()
};
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+ assert_eq!(layout, expected_layout);
}
#[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());
+fn layout_with_focused_panes() {
+ let kdl_layout = r#"
+ layout {
+ pane focus=true
+ }
+ "#;
+ 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()
+ }],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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);
+fn layout_with_pane_names() {
+ let kdl_layout = r#"
+ layout {
+ pane name="my awesome pane"
+ }
+ "#;
+ 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()
+ }],
+ ..Default::default()
+ }),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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,
- })),
- },
+fn layout_with_tab_names() {
+ let kdl_layout = r#"
+ layout {
+ tab name="my cool tab name 1"
+ tab name="my cool tab name 2"
+ }
+ "#;
+ 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()
+ },
+ ),
],
- split_size: None,
- run: None,
+ template: Some(PaneLayout::default()),
+ ..Default::default()
};
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+ assert_eq!(layout, expected_layout);
}
#[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,
- })),
- },
+fn layout_with_focused_tab() {
+ let kdl_layout = r#"
+ layout {
+ tab
+ tab focus=true
+ tab
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let expected_layout = Layout {
+ tabs: vec![
+ (None, PaneLayout::default()),
+ (None, PaneLayout::default()),
+ (None, PaneLayout::default()),
],
- split_size: None,
- run: None,
+ template: Some(PaneLayout::default()),
+ focused_tab_index: Some(1),
+ ..Default::default()
};
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+ assert_eq!(layout, expected_layout);
}
#[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());
+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 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()
+ },
+ ),
+ ],
+ template: Some(PaneLayout::default()),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
}
#[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);
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
#[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());
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
#[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());
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
#[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);
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
#[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,
- };
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+#[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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ assert!(layout.is_err(), "circular dependency detected");
}
#[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,
- };
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+#[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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ assert!(
+ layout.is_err(),
+ "error provided for more than one children block"
+ );
}
#[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());
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
#[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());
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ assert!(
+ layout.is_err(),
+ "error provided for more than one children block"
+ );
}
#[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);
+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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
#[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,
- };
+fn cannot_define_tab_template_name_with_space() {
+ let kdl_layout = r#"
+ layout {
+ tab_template name="with space" {
+ pane
+ children
+ pane
+ }
+ pane
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ 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 layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ assert!(layout.is_err(), "error provided for tab name with space");
+}
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+#[test]
+fn cannot_define_panes_and_tabs_on_same_level() {
+ let kdl_layout = r#"
+ layout {
+ pane
+ tab {
+ pane
+ }
+ }
+ "#;
+ 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"
+ );
}
#[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());
+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 layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into());
+ assert!(
+ layout.is_err(),
+ "{}",
+ format!(
+ "error provided for tab template name with keyword: {}",
+ keyword
+ )
+ );
+ }
}
#[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);
+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 layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into());
+ assert!(
+ layout.is_err(),
+ "{}",
+ format!(
+ "error provided for pane template name with keyword: {}",
+ keyword
+ )
+ );
+ }
}
#[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,
- };
+fn error_on_multiple_layout_nodes_in_file() {
+ 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));
+}
- assert_eq!(merged_layout, tab_layout.try_into().unwrap());
+#[test]
+fn error_on_unknown_layout_node() {
+ 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 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());
+fn error_on_unknown_layout_pane_property() {
+ 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 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;
+fn error_on_unknown_layout_pane_template_property() {
+ 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));
+}
- let expected_session = SessionFromYaml {
- name: Some(String::from("zellij-session")),
- attach: Some(true),
- };
+#[test]
+fn error_on_unknown_layout_tab_property() {
+ 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!(
+ "
+ 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!(
+ "
+ layout {{
+ pane_template {{
+ pane
+ children
+ 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!(
+ "
+ layout {{
+ tab_template {{
+ pane
+ children
+ pane
+ }}
+ }}
+ "
+ );
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ assert_snapshot!(format!("{:?}", layout_error));
+}
- assert_eq!(expected_session, session_layout);
+#[test]
+fn error_on_more_than_one_focused_tab() {
+ let kdl_layout = r#"
+ layout {
+ tab focus=true
+ tab focus=true
+ tab
+ }
+ "#;
+ 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
new file mode 100644
index 00000000..eab0dc6f
--- /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: 476
+expression: "format!(\"{:?}\", config_error)"
+---
+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
new file mode 100644
index 00000000..cce4254a
--- /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: 490
+expression: "format!(\"{:?}\", config_error)"
+---
+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__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 00000000..f82879d2
--- /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,139 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 848
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ 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,
+ },
+ 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,
+ },
+ ],
+ 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,
+ },
+ ],
+ 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,
+ },
+ ),
+}
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 00000000..003a3d41
--- /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,147 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 801
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ 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: 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,
+ },
+ 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,
+ },
+ ],
+ 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,
+ },
+ ),
+ ],
+ 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,
+ },
+ ),
+}
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 00000000..a964c655
--- /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,201 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 899
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ 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,
+ },
+ 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: 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,
+ },
+ ),
+ (
+ 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,
+ },
+ ),
+}
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 00000000..b640aa43
--- /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: 979
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Only one tab can be focused", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..0c2811ec
--- /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: 879
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Only one layout node per file allowed", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..cf7fc692
--- /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: 951
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Pane templates must have a name", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..c7b591f8
--- /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: 966
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Tab templates must have a name", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..b2b28ffe
--- /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: 892
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Unknown layout node: 'i_am_not_a_proper_node'", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..573ca75a
--- /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: 926
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Unknown pane property: spit_size", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..09701ebe
--- /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: 939
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Unknown pane property: spit_size", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), offset: Some(68), len: Some(11) })
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 00000000..bbe1769c
--- /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: 925
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Invalid tab property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), 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
new file mode 100644
index 00000000..134970f7
--- /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: 936
+expression: "format!(\"{:?}\", layout_error)"
+---
+KdlError(KdlError { error_message: "Invalid tab property 'spit_size'", src: Some(NamedSource { name: "layout_file_name", source: "<redacted>"), offset: Some(30), len: Some(48) })
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 00000000..f61145b4
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap
@@ -0,0 +1,231 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 549
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [
+ (
+ Some(
+ "my first 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: 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,
+ },
+ ),
+ (
+ 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: [
+ 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,
+ },
+ ),
+ (
+ 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,
+ },
+ 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: [
+ 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/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 00000000..77b7478c
--- /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,125 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 756
+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: [],
+ 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: [
+ 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,
+ },
+ ],
+ 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_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 00000000..fd12e42b
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap
@@ -0,0 +1,94 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 729
+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: [],
+ 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,
+ },
+ ],
+ 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_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 00000000..561c35f1
--- /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
new file mode 100644
index 00000000..72101338
--- /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,96 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 648
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ 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,
+ },
+ ),
+ ),
+ 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,
+ },
+ ],
+ 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,
+ },
+ ),
+}
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 00000000..26ee77f8
--- /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/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 00000000..26ee77f8
--- /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/input/unit/theme_test.rs b/zellij-utils/src/input/unit/theme_test.rs
index ad5b480a..56259201 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 insta::assert_snapshot;
+use std::path::{Path, PathBuf};
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 354107f5..93008788 100644
--- a/zellij-utils/src/ipc.rs
+++ b/zellij-utils/src/ipc.rs
@@ -3,10 +3,8 @@ use crate::{
cli::CliArgs,
data::{ClientId, InputMode, Style},
errors::{get_current_ctx, ErrorContext},
- input::{
- actions::Action, keybinds::Keybinds, layout::LayoutFromYaml, 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;
@@ -87,7 +85,7 @@ pub enum ClientToServerMsg {
ClientAttributes,
Box<CliArgs>,
Box<Options>,
- Box<LayoutFromYaml>,
+ Box<Layout>,
Option<PluginsConfig>,
),
AttachClient(ClientAttributes, Options),
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 00000000..4bf3aea6
--- /dev/null
+++ b/zellij-utils/src/kdl/kdl_layout_parser.rs
@@ -0,0 +1,888 @@
+use crate::input::{
+ command::RunCommand,
+ config::ConfigError,
+ layout::{Layout, PaneLayout, Run, RunPlugin, RunPluginLocation, SplitDirection, SplitSize},
+};
+
+use kdl::*;
+
+use std::collections::{HashMap, HashSet};
+use std::str::FromStr;
+
+use crate::{
+ 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,
+};
+
+use std::convert::TryFrom;
+use std::path::PathBuf;
+use std::vec::Vec;
+use url::Url;
+
+pub struct KdlLayoutParser<'a> {
+ raw_layout: &'a str,
+ tab_templates: HashMap<String, (PaneLayout, KdlNode)>,
+ pane_templates: HashMap<String, (PaneLayout, KdlNode)>,
+ default_tab_template: Option<(PaneLayout, KdlNode)>,
+}
+
+impl<'a> KdlLayoutParser<'a> {
+ pub fn new(raw_layout: &'a str) -> Self {
+ KdlLayoutParser {
+ raw_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"
+ || 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 == "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"
+ }
+ 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(),
+ ))
+ } 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(),
+ ))
+ } else {
+ Ok(())
+ }
+ }
+ fn parse_split_size(&self, kdl_node: &KdlNode) -> Result<Option<SplitSize>, 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 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") {
+ 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<Option<Run>, 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 location = RunPluginLocation::try_from(url)?;
+ Ok(Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd,
+ location,
+ })))
+ }
+ fn parse_pane_command(&self, pane_node: &KdlNode) -> Result<Option<Run>, 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 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(),
+ )
+ },
+ 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().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<Option<Run>, 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(),
+ ));
+ }
+ run = self.parse_plugin_block(plugin_block)?;
+ }
+ Ok(run)
+ }
+ fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result<PaneLayout, ConfigError> {
+ 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 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![]),
+ };
+ Ok(PaneLayout {
+ borderless: borderless.unwrap_or_default(),
+ focus,
+ name,
+ split_size,
+ run,
+ children_split_direction,
+ external_children_index,
+ children,
+ ..Default::default()
+ })
+ }
+ fn parse_pane_node_with_template(
+ &self,
+ kdl_node: &KdlNode,
+ mut pane_layout: PaneLayout,
+ pane_layout_kdl_node: &KdlNode,
+ ) -> Result<PaneLayout, ConfigError> {
+ 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) => {
+ 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, 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.external_children_index = None;
+ Ok(pane_layout)
+ }
+ fn parse_split_direction(&self, kdl_node: &KdlNode) -> Result<SplitDirection, ConfigError> {
+ 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> {
+ 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(),
+ ))?;
+ 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)?;
+ 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,
+ (
+ 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<String>, PaneLayout), ConfigError> {
+ // (is_focused, Option<tab_name>, 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 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<Vec<PaneLayout>, 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, 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,
+ )?);
+ } 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() {
+ nodes.push(PaneLayout::default());
+ }
+ Ok(nodes)
+ }
+ fn parse_child_pane_nodes_for_pane(
+ &self,
+ children: &[KdlNode],
+ ) -> Result<(Option<usize>, Vec<PaneLayout>), 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, 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,
+ )?);
+ } 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))
+ }
+ 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!("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(())
+ }
+ 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())
+ {
+ 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(())
+ }
+ 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::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> {
+ let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?;
+ if !successfully_inserted {
+ 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(
+ &self,
+ kdl_node: &KdlNode,
+ mut tab_layout: PaneLayout,
+ tab_layout_kdl_node: &KdlNode,
+ ) -> Result<(bool, Option<String>, PaneLayout), ConfigError> {
+ // (is_focused, Option<tab_name>, 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(),
+ };
+ match kdl_children_nodes!(kdl_node) {
+ Some(children) => {
+ 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(&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.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(),
+ ))?;
+ self.assert_legal_node_name(&template_name, 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)?, kdl_node.clone()));
+ Ok(())
+ }
+ fn parse_tab_template_node(&self, kdl_node: &KdlNode) -> Result<PaneLayout, ConfigError> {
+ 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 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" {
+ 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, 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,
+ )?);
+ } 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(),
+ ));
+ }
+ }
+ }
+ Ok(PaneLayout {
+ children_split_direction,
+ children: tab_children,
+ external_children_index,
+ ..Default::default()
+ })
+ }
+ fn default_template(&self) -> Result<Option<PaneLayout>, ConfigError> {
+ match &self.default_tab_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.external_children_index = None;
+ Ok(Some(template))
+ },
+ None => Ok(None),
+ }
+ }
+ pub fn get_pane_template_dependency_tree(
+ &self,
+ kdl_children: &'a [KdlNode],
+ ) -> Result<HashMap<&'a str, HashSet<&'a str>>, 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 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],
+ 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
+ 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::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);
+ 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<String>, PaneLayout)>,
+ focused_tab_index: Option<usize>,
+ ) -> Result<Layout, ConfigError> {
+ let template = self
+ .default_template()?
+ .unwrap_or_else(|| PaneLayout::default());
+
+ Ok(Layout {
+ tabs: tabs,
+ template: Some(template),
+ focused_tab_index,
+ ..Default::default()
+ })
+ }
+ fn layout_with_one_tab(&self, panes: Vec<PaneLayout>) -> Result<Layout, ConfigError> {
+ let main_tab_layout = PaneLayout {
+ children: panes,
+ ..Default::default()
+ };
+ let default_template = self.default_template()?;
+ 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
+ vec![]
+ } else {
+ 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 {
+ tabs,
+ template: Some(template),
+ ..Default::default()
+ })
+ }
+ fn layout_with_one_pane(&self) -> Result<Layout, ConfigError> {
+ 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<String>, PaneLayout)>,
+ child_panes: &mut Vec<PaneLayout>,
+ ) -> 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(),
+ ));
+ }
+ 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(),
+ ));
+ }
+ match &self.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,
+ default_tab_template_kdl_node,
+ )?);
+ },
+ None => {
+ child_tabs.push(self.parse_tab_node(child)?);
+ },
+ }
+ } 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(),
+ ));
+ }
+ 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(),
+ ));
+ }
+ 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(),
+ ));
+ }
+ Ok(())
+ }
+ pub fn parse(&mut self) -> Result<Layout, ConfigError> {
+ 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::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, &kdl_layout)?;
+ 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() {
+ 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(),
+ ));
+ }
+ let focused_tab_index = child_tabs.iter().position(|(is_focused, _, _)| *is_focused);
+ let child_tabs: Vec<(Option<String>, 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 {
+ self.layout_with_one_pane()
+ }
+ }
+}
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
new file mode 100644
index 00000000..7092653c
--- /dev/null
+++ b/zellij-utils/src/kdl/mod.rs
@@ -0,0 +1,1626 @@
+mod kdl_layout_parser;
+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::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 strum::IntoEnumIterator;
+use url::Url;
+
+use miette::NamedSource;
+
+use kdl::{KdlDocument, KdlEntry, KdlNode};
+
+use std::path::PathBuf;
+use std::str::FromStr;
+
+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(),
+ )),
+ }
+ }
+ }};
+}
+
+#[macro_export]
+macro_rules! parse_kdl_action_u8_arguments {
+ ( $action_name:expr, $action_arguments:expr, $action_node:expr ) => {{
+ let mut bytes = vec![];
+ 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(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())
+ };
+}
+
+#[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, $action_node:expr ) => {{
+ let mut chars_to_write = String::new();
+ 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(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(),
+ ))
+ },
+ },
+ 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()
+ };
+}
+
+#[macro_export]
+macro_rules! kdl_children_nodes {
+ ( $kdl_node:expr ) => {
+ $kdl_node.children().map(|c| c.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 ) => {
+ $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<Vec<_>, _> = $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())
+ }};
+}
+
+#[macro_export]
+macro_rules! kdl_argument_values {
+ ( $kdl_node:expr ) => {
+ $kdl_node.entries().iter().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]
+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(),
+ )
+ })
+ })
+ .collect::<Result<_, _>>()?
+ };
+}
+
+#[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::<Result<_, _>>()?
+ };
+}
+
+pub fn kdl_arguments_that_are_strings<'a>(
+ arguments: impl Iterator<Item = &'a KdlEntry>,
+) -> Result<Vec<String>, ConfigError> {
+ // pub fn kdl_arguments_that_are_strings <'a>(arguments: impl Iterator<Item=&'a KdlValue>) -> Result<Vec<String>, ConfigError> {
+ let mut args: Vec<String> = 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(),
+ ));
+ },
+ }
+ }
+ 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<u8>,
+ action_node: &KdlNode,
+ ) -> Result<Self, ConfigError> {
+ match action_name {
+ "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;
+ Ok(Action::GoToTab(tab_index))
+ },
+ _ => 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<Self, ConfigError> {
+ 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(),
+ ))
+ },
+ },
+ "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(),
+ )
+ })?;
+ 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(),
+ )
+ })?;
+ 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(),
+ )
+ })?;
+ 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(),
+ )
+ })?;
+ 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()).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()).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()).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(),
+ )),
+ }
+ }
+}
+
+impl TryFrom<(&str, &KdlDocument)> for PaletteColor {
+ type Error = ConfigError;
+
+ fn try_from(
+ (color_name, theme_colors): (&str, &KdlDocument),
+ ) -> Result<PaletteColor, Self::Error> {
+ 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,
+ }
+ };
+ 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(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;
+ 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(),
+ )
+ })?;
+ 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(),
+ ))?;
+ Ok(PaletteColor::EightBit(n as u8))
+ } else {
+ Err(ConfigError::new_kdl_error(
+ "Failed to parse color".into(),
+ color.span().offset(),
+ color.span().len(),
+ ))
+ }
+ }
+}
+
+impl TryFrom<&KdlNode> for Action {
+ // type Error = Box<dyn std::error::Error>;
+ type Error = ConfigError;
+ fn try_from(kdl_action: &KdlNode) -> Result<Self, Self::Error> {
+ 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)
+ },
+ "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, 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(),
+ ));
+ }
+ 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(ConfigError::new_kdl_error(
+ format!("Unsupported action: {}", action_name).into(),
+ kdl_action.span().offset(),
+ kdl_action.span().len(),
+ )),
+ }
+ }
+}
+
+#[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 => 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(),
+ ));
+ },
+ },
+ 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]
+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]
+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 ) => {
+ $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))
+ };
+}
+
+#[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_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_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 ) => {
+ $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_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 ) => {
+ $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 ) => {
+ $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) -> Result<Self, ConfigError> {
+ 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,
+ default_shell,
+ default_layout,
+ layout_dir,
+ theme_dir,
+ mouse_mode,
+ pane_frames,
+ mirror_session,
+ on_force_close,
+ scroll_buffer_size,
+ copy_command,
+ copy_clipboard,
+ copy_on_select,
+ scrollback_editor,
+ session_name,
+ attach_to_session,
+ })
+ }
+}
+
+impl RunPlugin {
+ pub fn from_kdl(kdl_node: &KdlNode) -> Result<Self, ConfigError> {
+ 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,
+ location,
+ })
+ }
+}
+impl Layout {
+ pub fn from_kdl(raw_layout: &str, file_name: String) -> Result<Self, ConfigError> {
+ 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) => {
+ let error_message = match kdl_error.kind {
+ kdl::KdlErrorKind::Context("valid node terminator") => {
+ 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")),
+ };
+ 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 {
+ pub fn from_kdl(kdl_env_variables: &KdlNode) -> Result<Self, ConfigError> {
+ let mut env: HashMap<String, String> = 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(),
+ ))?;
+ env.insert(env_var_name.into(), env_var_value);
+ }
+ Ok(EnvironmentVariables::from_data(env))
+ }
+}
+
+impl Keybinds {
+ fn bind_keys_in_block(
+ block: &KdlNode,
+ input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
+ ) -> 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");
+ for key_block in bind_nodes {
+ Keybinds::bind_actions_for_each_key(key_block, input_mode_keybinds)?;
+ }
+ // 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::new_kdl_error(
+ format!("Unknown keybind instruction: '{}'", kdl_name!(key_block)),
+ key_block.span().offset(),
+ key_block.span().len(),
+ ));
+ }
+ }
+ Ok(())
+ }
+ pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result<Self, ConfigError> {
+ 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).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) {
+ 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"
+ || 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)?;
+ 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)?;
+ };
+ Ok(keybinds_from_config)
+ }
+ fn bind_actions_for_each_key(
+ key_block: &KdlNode,
+ input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
+ ) -> Result<(), ConfigError> {
+ let keys: Vec<Key> = keys_from_kdl!(key_block);
+ let actions: Vec<Action> = 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<Key, Vec<Action>>,
+ ) -> Result<(), ConfigError> {
+ let keys: Vec<Key> = 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<Key> = 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<Key, Vec<Action>>, 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_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<Self, ConfigError> {
+ 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![],
+ };
+ Ok(RunCommand { command, args, cwd })
+ }
+}
+
+impl Config {
+ pub fn from_kdl(kdl_config: &str, base_config: Option<Config>) -> Result<Config, ConfigError> {
+ 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<Self, ConfigError> {
+ let mut plugins: HashMap<PluginTag, PluginConfig> = 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(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),
+ 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<UiConfig, ConfigError> {
+ 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<Self, ConfigError> {
+ let mut themes: HashMap<String, Theme> = HashMap::new();
+ for theme_config in kdl_children_nodes_or_error!(themes_from_kdl, "no themes found") {
+ let theme_name = kdl_name!(theme_config);
+ 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)
+ }
+}
+
+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.clone())?;
+ let mut kdl_config = String::new();
+ file.read_to_string(&mut kdl_config)?;
+ let kdl_config: KdlDocument = kdl_config.parse()?;
+ let kdl_themes = kdl_config.get("themes").ok_or(ConfigError::new_kdl_error(
+ "No theme node found in file".into(),
+ kdl_config.span().offset(),
+ kdl_config.span().len(),
+ ))?;
+ let all_themes_in_file = Themes::from_kdl(kdl_themes)?;
+ let 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/lib.rs b/zellij-utils/src/lib.rs
index e871aeee..892dcbc1 100644
--- a/zellij-utils/src/lib.rs
+++ b/zellij-utils/src/lib.rs
@@ -4,6 +4,7 @@ pub mod data;
pub mod envs;
pub mod errors;
pub mod input;
+pub mod kdl;
pub mod pane_size;
pub mod position;
pub mod setup;
@@ -19,8 +20,6 @@ pub mod logging; // Requires log4rs
#[cfg(not(target_family = "wasm"))]
pub use ::{
- async_std, clap, interprocess, lazy_static, libc, nix, regex, serde, serde_yaml, signal_hook,
+ anyhow, async_std, clap, interprocess, lazy_static, libc, nix, regex, serde, signal_hook,
tempfile, termwiz, vte,
};
-
-pub use anyhow;
diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs
index 2a9f54c0..007cf752 100644
--- a/zellij-utils/src/pane_size.rs
+++ b/zellij-utils/src/pane_size.rs
@@ -83,6 +83,29 @@ impl Dimension {
self.inner = inner;
}
+ 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) => {
+ 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/setup.rs b/zellij-utils/src/setup.rs
index 9b478e05..9972915e 100644
--- a/zellij-utils/src/setup.rs
+++ b/zellij-utils/src/setup.rs
@@ -1,42 +1,122 @@
-use crate::consts::{SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, ZELLIJ_PROJ_DIR};
-use clap::Args;
+use crate::input::theme::Theme;
+use crate::{
+ cli::{CliArgs, Command},
+ consts::{
+ FEATURES, SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION,
+ ZELLIJ_PROJ_DIR,
+ },
+ input::{
+ config::{Config, ConfigError},
+ layout::Layout,
+ options::Options,
+ },
+};
+use clap::{Args, IntoApp};
+use clap_complete::Shell;
use directories_next::BaseDirs;
use serde::{Deserialize, Serialize};
use std::{
- io::Write,
- path::{Path, PathBuf},
+ convert::TryFrom, fmt::Write as FmtWrite, io::Write, path::Path, path::PathBuf, process,
};
const CONFIG_LOCATION: &str = ".config/zellij";
+const CONFIG_NAME: &str = "config.kdl";
+static ARROW_SEPARATOR: &str = "";
+
+#[cfg(not(test))]
+/// Goes through a predefined list and checks for an already
+/// existing config directory, returns the first match
+pub fn find_default_config_dir() -> Option<PathBuf> {
+ default_config_dirs()
+ .into_iter()
+ .filter(|p| p.is_some())
+ .find(|p| p.clone().unwrap().exists())
+ .flatten()
+}
+
+#[cfg(test)]
+pub fn find_default_config_dir() -> Option<PathBuf> {
+ None
+}
+
+/// Order in which config directories are checked
+fn default_config_dirs() -> Vec<Option<PathBuf>> {
+ vec![
+ home_config_dir(),
+ Some(xdg_config_dir()),
+ Some(Path::new(SYSTEM_DEFAULT_CONFIG_DIR).to_path_buf()),
+ ]
+}
+
+/// Looks for an existing dir, uses that, else returns a
+/// dir matching the config spec.
+pub fn get_default_data_dir() -> PathBuf {
+ [
+ xdg_data_dir(),
+ Path::new(SYSTEM_DEFAULT_DATA_DIR_PREFIX).join("share/zellij"),
+ ]
+ .into_iter()
+ .find(|p| p.exists())
+ .unwrap_or_else(xdg_data_dir)
+}
+
+pub fn xdg_config_dir() -> PathBuf {
+ ZELLIJ_PROJ_DIR.config_dir().to_owned()
+}
+
+pub fn xdg_data_dir() -> PathBuf {
+ ZELLIJ_PROJ_DIR.data_dir().to_owned()
+}
+
+pub fn home_config_dir() -> Option<PathBuf> {
+ if let Some(user_dirs) = BaseDirs::new() {
+ let config_dir = user_dirs.home_dir().join(CONFIG_LOCATION);
+ Some(config_dir)
+ } else {
+ None
+ }
+}
+
+pub fn get_layout_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
+ config_dir.map(|dir| dir.join("layouts"))
+}
+
+pub fn get_theme_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
+ config_dir.map(|dir| dir.join("themes"))
+}
+pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> {
+ std::io::stdout().write_all(asset)?;
+ Ok(())
+}
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!(
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!(
@@ -63,15 +143,6 @@ pub const ZSH_AUTO_START_SCRIPT: &[u8] = include_bytes!(concat!(
"assets/shell/auto-start.zsh"
));
-pub fn get_theme_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
- config_dir.map(|dir| dir.join("themes"))
-}
-
-pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> {
- std::io::stdout().write_all(asset)?;
- Ok(())
-}
-
pub fn dump_default_config() -> std::io::Result<()> {
dump_asset(DEFAULT_CONFIG)
}
@@ -118,504 +189,431 @@ pub struct Setup {
pub generate_auto_start: Option<String>,
}
-#[cfg(test)]
-pub fn find_default_config_dir() -> Option<PathBuf> {
- None
-}
-
-#[cfg(not(test))]
-/// Goes through a predefined list and checks for an already
-/// existing config directory, returns the first match
-pub fn find_default_config_dir() -> Option<PathBuf> {
- default_config_dirs()
- .into_iter()
- .filter(|p| p.is_some())
- .find(|p| p.clone().unwrap().exists())
- .flatten()
-}
-
-/// Order in which config directories are checked
-#[allow(dead_code)]
-fn default_config_dirs() -> Vec<Option<PathBuf>> {
- vec![
- home_config_dir(),
- Some(xdg_config_dir()),
- Some(Path::new(SYSTEM_DEFAULT_CONFIG_DIR).to_path_buf()),
- ]
-}
-
-/// Looks for an existing dir, uses that, else returns a
-/// dir matching the config spec.
-pub fn get_default_data_dir() -> PathBuf {
- [
- xdg_data_dir(),
- Path::new(SYSTEM_DEFAULT_DATA_DIR_PREFIX).join("share/zellij"),
- ]
- .into_iter()
- .find(|p| p.exists())
- .unwrap_or_else(xdg_data_dir)
-}
-
-pub fn xdg_config_dir() -> PathBuf {
- ZELLIJ_PROJ_DIR.config_dir().to_owned()
-}
-
-pub fn xdg_data_dir() -> PathBuf {
- ZELLIJ_PROJ_DIR.data_dir().to_owned()
-}
-
-pub fn home_config_dir() -> Option<PathBuf> {
- if let Some(user_dirs) = BaseDirs::new() {
- let config_dir = user_dirs.home_dir().join(CONFIG_LOCATION);
- Some(config_dir)
- } else {
- None
- }
-}
-
-pub fn get_layout_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
- config_dir.map(|dir| dir.join("layouts"))
-}
-
-#[cfg(not(target_family = "wasm"))]
-pub use not_wasm::*;
-
-#[cfg(not(target_family = "wasm"))]
-mod not_wasm {
- use super::*;
- use crate::{
- cli::{CliArgs, Command},
- consts::{FEATURES, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION},
- input::{
- config::{Config, ConfigError},
- layout::{LayoutFromYaml, LayoutFromYamlIntermediate},
- options::Options,
- theme::ThemesFromYaml,
- },
- };
- use clap::IntoApp;
- use clap_complete::Shell;
- use std::{convert::TryFrom, fmt::Write as FmtWrite, io::Write, path::PathBuf, process};
-
- const CONFIG_NAME: &str = "config.yaml";
- static ARROW_SEPARATOR: &str = "";
-
- impl Setup {
- /// Entrypoint from main
- /// Merges options from the config file and the command line options
- /// into `[Options]`, the command line options superceeding the layout
- /// file options, superceeding the config file options:
- /// 1. command line options (`zellij options`)
- /// 2. layout options
- /// (`layout.yaml` / `zellij --layout`)
- /// 3. config options (`config.yaml`)
- pub fn from_options(
- opts: &CliArgs,
- ) -> Result<(Config, Option<LayoutFromYaml>, 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 mut config = if !clean {
- match Config::try_from(opts) {
- Ok(config) => config,
- Err(e) => {
- return Err(e);
- },
- }
+impl Setup {
+ /// Entrypoint from main
+ /// Merges options from the config file and the command line options
+ /// into `[Options]`, the command line options superceeding the layout
+ /// file options, superceeding the config file options:
+ /// 1. command line options (`zellij options`)
+ /// 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> {
+ 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 cli_config_options: Option<Options> =
+ if let Some(Command::Options(options)) = cli_args.command.clone() {
+ Some(options.into())
} else {
- Config::default()
- };
-
- 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());
- 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);
- },
+ 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(),
+ };
- if let Some(theme_dir) = config_options
- .theme_dir
- .clone()
- .or_else(|| get_theme_dir(opts.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 == "yaml" || extension == "yml" {
- if let Ok(themes) = ThemesFromYaml::from_path(&entry.path()) {
- match config.themes {
- Some(t) => config.themes = Some(t.merge(themes.into())),
- None => config.themes = Some(themes.into()),
- }
- }
+ 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" {
+ 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);
+ },
}
}
}
}
}
-
- if let Some(Command::Setup(ref setup)) = &opts.command {
- setup
- .from_cli_with_options(opts, &config_options)
- .map_or_else(
- |e| {
- eprintln!("{:?}", e);
- process::exit(1);
- },
- |_| {},
- );
- };
-
- Setup::merge_config_with_layout(config, layout, config_options)
}
- /// General setup helpers
- pub fn from_cli(&self) -> std::io::Result<()> {
- if self.clean {
- return Ok(());
- }
-
- if self.dump_config {
- dump_default_config()?;
- std::process::exit(0);
- }
+ if let Some(Command::Setup(ref setup)) = &cli_args.command {
+ setup
+ .from_cli_with_options(cli_args, &config_options)
+ .map_or_else(
+ |e| {
+ eprintln!("{:?}", e);
+ process::exit(1);
+ },
+ |_| {},
+ );
+ };
+ Ok((config, layout, config_options))
+ }
- if let Some(shell) = &self.generate_completion {
- Self::generate_completion(shell);
- std::process::exit(0);
- }
+ /// General setup helpers
+ pub fn from_cli(&self) -> std::io::Result<()> {
+ if self.clean {
+ return Ok(());
+ }
- if let Some(shell) = &self.generate_auto_start {
- Self::generate_auto_start(shell);
- std::process::exit(0);
- }
+ if self.dump_config {
+ dump_default_config()?;
+ std::process::exit(0);
+ }
- if let Some(layout) = &self.dump_layout {
- dump_specified_layout(layout)?;
- std::process::exit(0);
- }
+ if let Some(shell) = &self.generate_completion {
+ Self::generate_completion(shell);
+ std::process::exit(0);
+ }
- Ok(())
+ if let Some(shell) = &self.generate_auto_start {
+ Self::generate_auto_start(shell);
+ std::process::exit(0);
}
- /// Checks the merged configuration
- pub fn from_cli_with_options(
- &self,
- opts: &CliArgs,
- config_options: &Options,
- ) -> std::io::Result<()> {
- if self.check {
- Setup::check_defaults_config(opts, config_options)?;
- std::process::exit(0);
- }
- Ok(())
+ if let Some(layout) = &self.dump_layout {
+ dump_specified_layout(layout)?;
+ std::process::exit(0);
}
- fn merge_config_with_layout(
- config: Config,
- layout: Option<LayoutFromYamlIntermediate>,
- config_options: Options,
- ) -> Result<(Config, Option<LayoutFromYaml>, 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),
- };
+ Ok(())
+ }
- 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()?);
- (config, config_options)
- } else {
- (config, config_options)
- };
- Ok((config, layout, config_options))
+ /// Checks the merged configuration
+ pub fn from_cli_with_options(
+ &self,
+ opts: &CliArgs,
+ config_options: &Options,
+ ) -> std::io::Result<()> {
+ if self.check {
+ Setup::check_defaults_config(opts, config_options)?;
+ std::process::exit(0);
}
+ Ok(())
+ }
- 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);
- let plugin_dir = data_dir.join("plugins");
- let layout_dir = config_options
- .layout_dir
- .clone()
- .or_else(|| get_layout_dir(config_dir.clone()));
- let theme_dir = config_options
- .theme_dir
- .clone()
- .or_else(|| get_theme_dir(config_dir.clone()));
- let system_data_dir =
- PathBuf::from(SYSTEM_DEFAULT_DATA_DIR_PREFIX).join("share/zellij");
- let config_file = opts
- .config
- .clone()
- .or_else(|| config_dir.clone().map(|p| p.join(CONFIG_NAME)));
+ 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);
+ let plugin_dir = data_dir.join("plugins");
+ let layout_dir = config_options
+ .layout_dir
+ .clone()
+ .or_else(|| get_layout_dir(config_dir.clone()));
+ let system_data_dir = PathBuf::from(SYSTEM_DEFAULT_DATA_DIR_PREFIX).join("share/zellij");
+ let config_file = opts
+ .config
+ .clone()
+ .or_else(|| config_dir.clone().map(|p| p.join(CONFIG_NAME)));
- // according to
- // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
- let hyperlink_start = "\u{1b}]8;;";
- let hyperlink_mid = "\u{1b}\\";
- let hyperlink_end = "\u{1b}]8;;\u{1b}\\";
+ // according to
+ // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+ let hyperlink_start = "\u{1b}]8;;";
+ let hyperlink_mid = "\u{1b}\\";
+ let hyperlink_end = "\u{1b}]8;;\u{1b}\\";
- let mut message = String::new();
+ let mut message = String::new();
- writeln!(&mut message, "[Version]: {:?}", VERSION).unwrap();
- if let Some(config_dir) = config_dir {
- writeln!(&mut message, "[CONFIG DIR]: {:?}", config_dir).unwrap();
- } else {
- message.push_str("[CONFIG DIR]: Not Found\n");
- let mut default_config_dirs = default_config_dirs()
- .iter()
- .filter_map(|p| p.clone())
- .collect::<Vec<PathBuf>>();
- default_config_dirs.dedup();
- message.push_str(
- " On your system zellij looks in the following config directories by default:\n",
- );
- for dir in default_config_dirs {
- writeln!(&mut message, " {:?}", dir).unwrap();
- }
- }
- if let Some(config_file) = config_file {
- writeln!(&mut message, "[CONFIG FILE]: {:?}", config_file).unwrap();
- match Config::new(&config_file) {
- Ok(_) => message.push_str("[CONFIG FILE]: Well defined.\n"),
- Err(e) => writeln!(&mut message, "[CONFIG ERROR]: {}", e).unwrap(),
- }
- } else {
- message.push_str("[CONFIG FILE]: Not Found\n");
- writeln!(
- &mut message,
- " By default zellij looks for a file called [{}] in the configuration directory",
- CONFIG_NAME
- )
- .unwrap();
- }
- writeln!(&mut message, "[DATA DIR]: {:?}", data_dir).unwrap();
- message.push_str(&format!("[PLUGIN DIR]: {:?}\n", plugin_dir));
- if let Some(layout_dir) = layout_dir {
- writeln!(&mut message, "[LAYOUT DIR]: {:?}", layout_dir).unwrap();
- } else {
- message.push_str("[LAYOUT DIR]: Not Found\n");
+ writeln!(&mut message, "[Version]: {:?}", VERSION).unwrap();
+ if let Some(config_dir) = config_dir {
+ writeln!(&mut message, "[CONFIG DIR]: {:?}", config_dir).unwrap();
+ } else {
+ message.push_str("[CONFIG DIR]: Not Found\n");
+ let mut default_config_dirs = default_config_dirs()
+ .iter()
+ .filter_map(|p| p.clone())
+ .collect::<Vec<PathBuf>>();
+ default_config_dirs.dedup();
+ message.push_str(
+ " On your system zellij looks in the following config directories by default:\n",
+ );
+ for dir in default_config_dirs {
+ writeln!(&mut message, " {:?}", dir).unwrap();
}
- if let Some(theme_dir) = theme_dir {
- writeln!(&mut message, "[THEME DIR]: {:?}", theme_dir).unwrap();
- } else {
- message.push_str("[THEME DIR]: Not Found\n");
+ }
+ if let Some(config_file) = config_file {
+ writeln!(&mut message, "[CONFIG FILE]: {:?}", config_file).unwrap();
+ // 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(),
}
- writeln!(&mut message, "[SYSTEM DATA DIR]: {:?}", system_data_dir).unwrap();
-
- writeln!(&mut message, "[ARROW SEPARATOR]: {}", ARROW_SEPARATOR).unwrap();
- message.push_str(" Is the [ARROW_SEPARATOR] displayed correctly?\n");
- message.push_str(" If not you may want to either start zellij with a compatible mode: 'zellij options --simplified-ui true'\n");
- let mut hyperlink_compat = String::new();
- hyperlink_compat.push_str(hyperlink_start);
- hyperlink_compat.push_str("https://zellij.dev/documentation/compatibility.html#the-status-bar-fonts-dont-render-correctly");
- hyperlink_compat.push_str(hyperlink_mid);
- hyperlink_compat.push_str("https://zellij.dev/documentation/compatibility.html#the-status-bar-fonts-dont-render-correctly");
- hyperlink_compat.push_str(hyperlink_end);
- write!(
+ } else {
+ message.push_str("[CONFIG FILE]: Not Found\n");
+ writeln!(
&mut message,
- " Or check the font that is in use:\n {}\n",
- hyperlink_compat
+ " By default zellij looks for a file called [{}] in the configuration directory",
+ CONFIG_NAME
)
.unwrap();
- message.push_str("[MOUSE INTERACTION]: \n");
- message.push_str(" Can be temporarily disabled through pressing the [SHIFT] key.\n");
- message.push_str(" If that doesn't fix any issues consider to disable the mouse handling of zellij: 'zellij options --disable-mouse-mode'\n");
-
- let default_editor = std::env::var("EDITOR")
- .or_else(|_| std::env::var("VISUAL"))
- .unwrap_or_else(|_| String::from("Not set, checked $EDITOR and $VISUAL"));
- writeln!(&mut message, "[DEFAULT EDITOR]: {}", default_editor).unwrap();
- writeln!(&mut message, "[FEATURES]: {:?}", FEATURES).unwrap();
- let mut hyperlink = String::new();
- hyperlink.push_str(hyperlink_start);
- hyperlink.push_str("https://www.zellij.dev/documentation/");
- hyperlink.push_str(hyperlink_mid);
- hyperlink.push_str("zellij.dev/documentation");
- hyperlink.push_str(hyperlink_end);
- writeln!(&mut message, "[DOCUMENTATION]: {}", hyperlink).unwrap();
- //printf '\e]8;;http://example.com\e\\This is a link\e]8;;\e\\\n'
-
- std::io::stdout().write_all(message.as_bytes())?;
-
- Ok(())
}
- fn generate_completion(shell: &str) {
- let shell: Shell = match shell.to_lowercase().parse() {
- Ok(shell) => shell,
- _ => {
- eprintln!("Unsupported shell: {}", shell);
- std::process::exit(1);
- },
- };
- let mut out = std::io::stdout();
- clap_complete::generate(shell, &mut CliArgs::command(), "zellij", &mut out);
- // add shell dependent extra completion
- match shell {
- Shell::Bash => {},
- Shell::Elvish => {},
- Shell::Fish => {
- let _ = out.write_all(FISH_EXTRA_COMPLETION);
- },
- Shell::PowerShell => {},
- Shell::Zsh => {},
- _ => {},
- };
+ writeln!(&mut message, "[DATA DIR]: {:?}", data_dir).unwrap();
+ message.push_str(&format!("[PLUGIN DIR]: {:?}\n", plugin_dir));
+ if let Some(layout_dir) = layout_dir {
+ writeln!(&mut message, "[LAYOUT DIR]: {:?}", layout_dir).unwrap();
+ } else {
+ message.push_str("[LAYOUT DIR]: Not Found\n");
}
+ writeln!(&mut message, "[SYSTEM DATA DIR]: {:?}", system_data_dir).unwrap();
- fn generate_auto_start(shell: &str) {
- let shell: Shell = match shell.to_lowercase().parse() {
- Ok(shell) => shell,
- _ => {
- eprintln!("Unsupported shell: {}", shell);
- std::process::exit(1);
- },
- };
+ writeln!(&mut message, "[ARROW SEPARATOR]: {}", ARROW_SEPARATOR).unwrap();
+ message.push_str(" Is the [ARROW_SEPARATOR] displayed correctly?\n");
+ message.push_str(" If not you may want to either start zellij with a compatible mode: 'zellij options --simplified-ui true'\n");
+ let mut hyperlink_compat = String::new();
+ hyperlink_compat.push_str(hyperlink_start);
+ hyperlink_compat.push_str("https://zellij.dev/documentation/compatibility.html#the-status-bar-fonts-dont-render-correctly");
+ hyperlink_compat.push_str(hyperlink_mid);
+ hyperlink_compat.push_str("https://zellij.dev/documentation/compatibility.html#the-status-bar-fonts-dont-render-correctly");
+ hyperlink_compat.push_str(hyperlink_end);
+ write!(
+ &mut message,
+ " Or check the font that is in use:\n {}\n",
+ hyperlink_compat
+ )
+ .unwrap();
+ message.push_str("[MOUSE INTERACTION]: \n");
+ message.push_str(" Can be temporarily disabled through pressing the [SHIFT] key.\n");
+ message.push_str(" If that doesn't fix any issues consider to disable the mouse handling of zellij: 'zellij options --disable-mouse-mode'\n");
- let mut out = std::io::stdout();
- match shell {
- Shell::Bash => {
- let _ = out.write_all(BASH_AUTO_START_SCRIPT);
- },
- Shell::Fish => {
- let _ = out.write_all(FISH_AUTO_START_SCRIPT);
- },
- Shell::Zsh => {
- let _ = out.write_all(ZSH_AUTO_START_SCRIPT);
- },
- _ => {},
- }
- }
- }
+ let default_editor = std::env::var("EDITOR")
+ .or_else(|_| std::env::var("VISUAL"))
+ .unwrap_or_else(|_| String::from("Not set, checked $EDITOR and $VISUAL"));
+ writeln!(&mut message, "[DEFAULT EDITOR]: {}", default_editor).unwrap();
+ writeln!(&mut message, "[FEATURES]: {:?}", FEATURES).unwrap();
+ let mut hyperlink = String::new();
+ hyperlink.push_str(hyperlink_start);
+ hyperlink.push_str("https://www.zellij.dev/documentation/");
+ hyperlink.push_str(hyperlink_mid);
+ hyperlink.push_str("zellij.dev/documentation");
+ hyperlink.push_str(hyperlink_end);
+ writeln!(&mut message, "[DOCUMENTATION]: {}", hyperlink).unwrap();
+ //printf '\e]8;;http://example.com\e\\This is a link\e]8;;\e\\\n'
- #[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))
- }
+ std::io::stdout().write_all(message.as_bytes())?;
- #[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);
- }
+ Ok(())
+ }
+ fn generate_completion(shell: &str) {
+ let shell: Shell = match shell.to_lowercase().parse() {
+ Ok(shell) => shell,
+ _ => {
+ eprintln!("Unsupported shell: {}", shell);
+ std::process::exit(1);
+ },
+ };
+ let mut out = std::io::stdout();
+ clap_complete::generate(shell, &mut CliArgs::command(), "zellij", &mut out);
+ // add shell dependent extra completion
+ match shell {
+ Shell::Bash => {},
+ Shell::Elvish => {},
+ Shell::Fish => {
+ let _ = out.write_all(FISH_EXTRA_COMPLETION);
+ },
+ Shell::PowerShell => {},
+ Shell::Zsh => {},
+ _ => {},
+ };
+ }
- #[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);
- }
+ fn generate_auto_start(shell: &str) {
+ let shell: Shell = match shell.to_lowercase().parse() {
+ Ok(shell) => shell,
+ _ => {
+ eprintln!("Unsupported shell: {}", shell);
+ std::process::exit(1);
+ },
+ };
- #[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);
+ let mut out = std::io::stdout();
+ match shell {
+ Shell::Bash => {
+ let _ = out.write_all(BASH_AUTO_START_SCRIPT);
+ },
+ Shell::Fish => {
+ let _ = out.write_all(FISH_AUTO_START_SCRIPT);
+ },
+ Shell::Zsh => {
+ let _ = out.write_all(ZSH_AUTO_START_SCRIPT);
+ },
+ _ => {},
}
+ }
+ 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())
+ })
+ .or_else(|| config.options.default_layout.clone());
+ // we merge-override the config here because the layout might contain configuration
+ // that needs to take precedence
+ 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 {
+ setup.from_cli().map_or_else(
+ |e| {
+ eprintln!("{:?}", e);
+ process::exit(1);
+ },
+ |_| {},
+ );
+ };
+ }
+}
- #[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);
- }
+#[cfg(test)]
+mod setup_test {
+ use super::Setup;
+ use crate::cli::{CliArgs, Command};
+ use crate::input::options::{CliOptions, Options};
+ use insta::assert_snapshot;
+ use std::path::PathBuf;
- #[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);
- }
+ #[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 00000000..00801403
--- /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 00000000..263f3c60
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap
@@ -0,0 +1,21 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 509
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ 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,
+ },
+ ),
+}
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 00000000..a36a7543
--- /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 00000000..d4ea72ea
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap
@@ -0,0 +1,82 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 492
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ 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",
+ ),
+ ),
+ },
+ ),
+ ),
+ 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",
+ ),
+ ),
+ },
+ ),
+ ),
+ borderless: true,
+ 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/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 00000000..e23c0d24
--- /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 00000000..35cd5f7e
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
@@ -0,0 +1,3363 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 491
+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(
+ 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,
+ ),
+ PaneNameInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ SearchInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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,
+ ),
+ ],
+ },
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ Tab,
+ ),
+ ],
+ },
+ RenamePane: {
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ 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(
+ 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(
+ 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,
+ ),
+ ],
+ },
+ 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(
+ 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(
+ Down,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ '%',
+ ): [
+ NewPane(
+ Some(
+ Right,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ ',',
+ ): [
+ SwitchToMode(
+ RenameTab,
+ ),
+ ],
+ Char(
+ '[',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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(
+ 'o',
+ ): [
+ SwitchToMode(
+ Session,
+ ),
+ ],
+ Ctrl(
+ 'p',
+ ): [
+ SwitchToMode(
+ Pane,
+ ),
+ ],
+ Ctrl(
+ 'q',
+ ): [
+ Quit,
+ ],
+ Ctrl(
+ 's',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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 00000000..07c91c5a
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap
@@ -0,0 +1,3367 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 541
+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(
+ 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,
+ ),
+ PaneNameInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ SearchInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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,
+ ),
+ ],
+ },
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ Tab,
+ ),
+ ],
+ },
+ RenamePane: {
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ 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(
+ 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(
+ 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,
+ ),
+ ],
+ },
+ 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(
+ 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(
+ Down,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ '%',
+ ): [
+ NewPane(
+ Some(
+ Right,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ ',',
+ ): [
+ SwitchToMode(
+ RenameTab,
+ ),
+ ],
+ Char(
+ '[',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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(
+ 'o',
+ ): [
+ SwitchToMode(
+ Session,
+ ),
+ ],
+ Ctrl(
+ 'p',
+ ): [
+ SwitchToMode(
+ Pane,
+ ),
+ ],
+ Ctrl(
+ 'q',
+ ): [
+ Quit,
+ ],
+ Ctrl(
+ 's',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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 00000000..fd8007af
--- /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 00000000..3a4e696d
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap
@@ -0,0 +1,21 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 492
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ 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,
+ },
+ ),
+}
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 00000000..d8fc7879
--- /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 00000000..5a71f06a
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap
@@ -0,0 +1,3377 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 557
+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(
+ 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,
+ ),
+ PaneNameInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ SearchInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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,
+ ),
+ ],
+ },
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ Tab,
+ ),
+ ],
+ },
+ RenamePane: {
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ 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(
+ 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(
+ 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,
+ ),
+ ],
+ },
+ 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(
+ 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(
+ Down,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ '%',
+ ): [
+ NewPane(
+ Some(
+ Right,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ ',',
+ ): [
+ SwitchToMode(
+ RenameTab,
+ ),
+ ],
+ Char(
+ '[',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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(
+ 'o',
+ ): [
+ SwitchToMode(
+ Session,
+ ),
+ ],
+ Ctrl(
+ 'p',
+ ): [
+ SwitchToMode(
+ Pane,
+ ),
+ ],
+ Ctrl(
+ 'q',
+ ): [
+ Quit,
+ ],
+ Ctrl(
+ 's',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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 00000000..1af931ce
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap
@@ -0,0 +1,3667 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 565
+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(
+ 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,
+ ),
+ PaneNameInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ SearchInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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,
+ ),
+ ],
+ },
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ Tab,
+ ),
+ ],
+ },
+ RenamePane: {
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ 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(
+ 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(
+ 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,
+ ),
+ ],
+ },
+ 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(
+ 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(
+ Down,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ '%',
+ ): [
+ NewPane(
+ Some(
+ Right,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ ',',
+ ): [
+ SwitchToMode(
+ RenameTab,
+ ),
+ ],
+ Char(
+ '[',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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(
+ 'o',
+ ): [
+ SwitchToMode(
+ Session,
+ ),
+ ],
+ Ctrl(
+ 'p',
+ ): [
+ SwitchToMode(
+ Pane,
+ ),
+ ],
+ Ctrl(
+ 'q',
+ ): [
+ Quit,
+ ],
+ Ctrl(
+ 's',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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 00000000..1e19ab48
--- /dev/null
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap
@@ -0,0 +1,3363 @@
+---
+source: zellij-utils/src/setup.rs
+assertion_line: 549
+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(
+ 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,
+ ),
+ PaneNameInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ SearchInput(
+ [
+ 0,
+ ],
+ ),
+ ],
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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,
+ ),
+ ],
+ },
+ 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,
+ ),
+ ],
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ Tab,
+ ),
+ ],
+ },
+ RenamePane: {
+ 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(
+ 'c',
+ ): [
+ SwitchToMode(
+ 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(
+ 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(
+ 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(
+ 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,
+ ),
+ ],
+ },
+ 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(
+ 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(
+ Down,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ '%',
+ ): [
+ NewPane(
+ Some(
+ Right,
+ ),
+ ),
+ SwitchToMode(
+ Normal,
+ ),
+ ],
+ Char(
+ ',',
+ ): [
+ SwitchToMode(
+ RenameTab,
+ ),
+ ],
+ Char(
+ '[',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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(
+ 'o',
+ ): [
+ SwitchToMode(
+ Session,
+ ),
+ ],
+ Ctrl(
+ 'p',
+ ): [
+ SwitchToMode(
+ Pane,
+ ),
+ ],
+ Ctrl(
+ 'q',
+ ): [
+ Quit,
+ ],
+ Ctrl(
+ 's',
+ ): [
+ SwitchToMode(
+ Scroll,
+ ),
+ ],
+ 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 00000000..87c94e6c
--- /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 00000000..84f98de5
--- /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 00000000..513e4de0
--- /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 00000000..e0ed0aa3
--- /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 00000000..fbd518fa
--- /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 00000000..0ef6721a
--- /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 00000000..62c5cd8b
--- /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 00000000..408003b1
--- /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 00000000..95b177ab
--- /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 00000000..7fc57aa0
--- /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 00000000..dfaeee91
--- /dev/null
+++ b/zellij-utils/src/test-fixtures/layout-with-ui-config.kdl
@@ -0,0 +1,6 @@
+layout
+ui {
+ pane_frames {
+ rounded_corners true
+ }
+}