aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-06-14 17:11:02 +0200
committerGitHub <noreply@github.com>2024-06-14 17:11:02 +0200
commit1f0ae94f01647eab66cabbd11434ecae1fac8da3 (patch)
treeb568f9ffc2064f4bdfd9c02c0aa5e581a3d1e750
parentdocs(readme): add name origin section (#3391) (diff)
downloadsl0ppij-1f0ae94f01647eab66cabbd11434ecae1fac8da3.tar.gz
sl0ppij-1f0ae94f01647eab66cabbd11434ecae1fac8da3.tar.xz
sl0ppij-1f0ae94f01647eab66cabbd11434ecae1fac8da3.zip
feat(plugins): rebind keys at runtime (#3422)
* refactor(server): interpret keys on server so they can be rebound * feat(plugins): allow rebinding keys at runtime * various cleanups * add tests * style(fmt): rustfmt * fix(tests): address (some) e2e test flakiness * style(fmt): rustfmt
-rw-r--r--default-plugins/fixture-plugin-for-tests/src/main.rs13
-rw-r--r--src/tests/e2e/cases.rs69
-rw-r--r--src/tests/e2e/remote_runner.rs2
-rw-r--r--src/tests/e2e/steps.rs2
-rw-r--r--zellij-client/src/input_handler.rs26
-rw-r--r--zellij-client/src/lib.rs17
-rw-r--r--zellij-client/src/unit/snapshots/zellij_client__input_handler__input_handler_tests__pixel_info_sent_to_server.snap6
-rw-r--r--zellij-client/src/unit/snapshots/zellij_client__stdin_tests__pixel_info_sent_to_server.snap6
-rw-r--r--zellij-client/src/unit/stdin_tests.rs320
-rw-r--r--zellij-server/src/lib.rs119
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs81
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap3
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rebind_keys_plugin_command.snap11
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap3
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs12
-rw-r--r--zellij-server/src/route.rs52
-rw-r--r--zellij-server/src/screen.rs32
-rw-r--r--zellij-server/src/unit/screen_tests.rs46
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_switch_mode_action.snap6
-rw-r--r--zellij-tile/src/shim.rs8
-rw-r--r--zellij-utils/assets/prost/api.plugin_command.rs7
-rw-r--r--zellij-utils/assets/prost/api.plugin_permission.rs3
-rw-r--r--zellij-utils/src/data.rs7
-rw-r--r--zellij-utils/src/errors.rs4
-rw-r--r--zellij-utils/src/ipc.rs4
-rw-r--r--zellij-utils/src/kdl/mod.rs16
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto2
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs10
-rw-r--r--zellij-utils/src/plugin_api/plugin_permission.proto1
-rw-r--r--zellij-utils/src/plugin_api/plugin_permission.rs2
30 files changed, 463 insertions, 427 deletions
diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs
index f504eec8..b6cac35b 100644
--- a/default-plugins/fixture-plugin-for-tests/src/main.rs
+++ b/default-plugins/fixture-plugin-for-tests/src/main.rs
@@ -56,6 +56,7 @@ impl ZellijPlugin for State {
PermissionType::WebAccess,
PermissionType::ReadCliPipes,
PermissionType::MessageAndLaunchOtherPlugins,
+ PermissionType::RebindKeys,
]);
self.configuration = configuration;
subscribe(&[
@@ -318,6 +319,18 @@ impl ZellijPlugin for State {
Some(std::path::PathBuf::from("/tmp")),
);
},
+ BareKey::Char('0') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
+ rebind_keys(
+ "
+ keybinds {
+ locked {
+ bind \"a\" { NewTab; }
+ }
+ }
+ "
+ .to_owned(),
+ );
+ },
_ => {},
},
Event::CustomMessage(message, payload) => {
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
index 6b1c12c6..9687d53c 100644
--- a/src/tests/e2e/cases.rs
+++ b/src/tests/e2e/cases.rs
@@ -162,6 +162,7 @@ pub fn split_terminals_vertically() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
// back to normal mode after split
step_is_complete = true;
@@ -205,6 +206,7 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(3, 2) {
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
// back to normal mode after split
step_is_complete = true;
@@ -259,6 +261,7 @@ pub fn scrolling_inside_a_pane() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -286,6 +289,7 @@ pub fn scrolling_inside_a_pane() {
{
// all lines have been written to the pane
remote_terminal.send_key(&SCROLL_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SCROLL_UP_IN_SCROLL_MODE);
step_is_complete = true;
}
@@ -337,6 +341,7 @@ pub fn toggle_pane_fullscreen() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -350,6 +355,7 @@ pub fn toggle_pane_fullscreen() {
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE);
step_is_complete = true;
}
@@ -398,6 +404,7 @@ pub fn open_new_tab() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -411,6 +418,7 @@ pub fn open_new_tab() {
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
step_is_complete = true;
}
@@ -463,6 +471,7 @@ pub fn close_tab() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -476,6 +485,7 @@ pub fn close_tab() {
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
step_is_complete = true;
}
@@ -493,6 +503,7 @@ pub fn close_tab() {
{
// cursor is in the newly opened second tab
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&CLOSE_TAB_IN_TAB_MODE);
step_is_complete = true;
}
@@ -653,6 +664,7 @@ pub fn close_pane() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -666,6 +678,7 @@ pub fn close_pane() {
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE);
step_is_complete = true;
}
@@ -751,6 +764,7 @@ pub fn closing_last_pane_exits_zellij() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE);
step_is_complete = true;
}
@@ -795,6 +809,7 @@ pub fn typing_exit_closes_pane() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -807,9 +822,13 @@ pub fn typing_exit_closes_pane() {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
remote_terminal.send_key("e".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("x".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("i".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("t".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("\n".as_bytes());
step_is_complete = true;
}
@@ -859,6 +878,7 @@ pub fn resize_pane() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -872,6 +892,7 @@ pub fn resize_pane() {
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&RESIZE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&RESIZE_LEFT_IN_RESIZE_MODE);
// back to normal mode
remote_terminal.send_key(&ENTER);
@@ -933,7 +954,9 @@ pub fn lock_mode() {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("INTERFACE LOCKED") {
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("abc".as_bytes());
step_is_complete = true;
}
@@ -983,6 +1006,7 @@ pub fn resize_terminal_window() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1046,6 +1070,7 @@ pub fn detach_and_attach_session() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1070,6 +1095,7 @@ pub fn detach_and_attach_session() {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(77, 2) {
remote_terminal.send_key(&SESSION_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&DETACH_IN_SESSION_MODE);
// text has been entered
step_is_complete = true;
@@ -1286,6 +1312,7 @@ fn focus_pane_with_mouse() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1345,6 +1372,7 @@ pub fn scrolling_inside_a_pane_with_mouse() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1420,6 +1448,7 @@ pub fn start_without_pane_frames() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 1)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1489,6 +1518,7 @@ pub fn mirrored_sessions() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1502,6 +1532,7 @@ pub fn mirrored_sessions() {
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
step_is_complete = true;
}
@@ -1544,6 +1575,7 @@ pub fn mirrored_sessions() {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("some text") {
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&MOVE_FOCUS_LEFT_IN_PANE_MODE); // same key as tab mode
step_is_complete = true;
}
@@ -1726,6 +1758,7 @@ pub fn multiple_users_in_different_panes_and_same_tab() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
step_is_complete = true;
}
@@ -1817,6 +1850,7 @@ pub fn multiple_users_in_different_tabs() {
if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
step_is_complete = true;
}
@@ -1839,6 +1873,7 @@ pub fn multiple_users_in_different_tabs() {
&& remote_terminal.snapshot_contains("Tab #1 [ ]")
&& remote_terminal.snapshot_contains("Tab #2")
&& remote_terminal.status_bar_appears()
+ && !remote_terminal.snapshot_contains("AND:")
{
// cursor is in the newly opened second tab
step_is_complete = true;
@@ -1899,11 +1934,17 @@ pub fn bracketed_paste() {
&& remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&BRACKETED_PASTE_START);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("a".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("b".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key("c".as_bytes());
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&BRACKETED_PASTE_END);
step_is_complete = true;
}
@@ -1952,6 +1993,7 @@ pub fn toggle_floating_panes() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&TOGGLE_FLOATING_PANES);
// back to normal mode after split
step_is_complete = true;
@@ -2002,6 +2044,7 @@ pub fn tmux_mode() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&TMUX_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPLIT_RIGHT_IN_TMUX_MODE);
// back to normal mode after split
step_is_complete = true;
@@ -2050,6 +2093,7 @@ pub fn edit_scrollback() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&SCROLL_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&EDIT_SCROLLBACK);
step_is_complete = true;
}
@@ -2099,8 +2143,13 @@ pub fn undo_rename_tab() {
&& remote_terminal.snapshot_contains("Tab #1")
{
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&RENAME_TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&[97, 97]);
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ remote_terminal.send_key(&ESC);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&ESC);
step_is_complete = true;
}
@@ -2113,7 +2162,9 @@ pub fn undo_rename_tab() {
name: "Wait for tab name to apper on screen",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.snapshot_contains("Tab #1") {
+ if remote_terminal.snapshot_contains("Tab #1")
+ && remote_terminal.snapshot_contains("Tip:")
+ {
step_is_complete = true
}
step_is_complete
@@ -2149,8 +2200,13 @@ pub fn undo_rename_pane() {
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&RENAME_PANE_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&[97, 97]);
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ remote_terminal.send_key(&ESC);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&ESC);
step_is_complete = true;
}
@@ -2163,7 +2219,9 @@ pub fn undo_rename_pane() {
name: "Wait for pane name to apper on screen",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.snapshot_contains("Pane #1") {
+ if remote_terminal.snapshot_contains("Pane #1")
+ && remote_terminal.snapshot_contains("Tip:")
+ {
step_is_complete = true
}
step_is_complete
@@ -2212,6 +2270,7 @@ pub fn send_command_through_the_cli() {
"{}/append-echo-script.sh",
fixture_folder
));
+ std::thread::sleep(std::time::Duration::from_millis(100));
step_is_complete = true;
}
step_is_complete
@@ -2221,11 +2280,7 @@ pub fn send_command_through_the_cli() {
name: "Initial run of suspended command",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
- if remote_terminal.snapshot_contains("<Ctrl-c>")
- && remote_terminal.cursor_position_is(0, 0)
- // cursor does not appear in
- // suspend_start panes
- {
+ if remote_terminal.snapshot_contains("<Ctrl-c>") {
std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&SPACE); // run script - here we use SPACE
// instead of the default ENTER because
diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs
index ab2c4a9e..09f23991 100644
--- a/src/tests/e2e/remote_runner.rs
+++ b/src/tests/e2e/remote_runner.rs
@@ -261,7 +261,7 @@ fn read_from_channel(
break;
}
if should_sleep {
- std::thread::sleep(std::time::Duration::from_millis(10));
+ std::thread::sleep(std::time::Duration::from_millis(100));
should_sleep = false;
}
let mut buf = [0u8; 1280000];
diff --git a/src/tests/e2e/steps.rs b/src/tests/e2e/steps.rs
index c09fc939..85009e19 100644
--- a/src/tests/e2e/steps.rs
+++ b/src/tests/e2e/steps.rs
@@ -11,6 +11,7 @@ pub fn new_tab() -> Step {
let mut step_is_complete = false;
if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
remote_terminal.send_key(&TAB_MODE);
+ std::thread::sleep(std::time::Duration::from_millis(100));
remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE);
step_is_complete = true;
}
@@ -37,6 +38,7 @@ pub fn move_tab_left() -> Step {
let mut step_is_complete = false;
if remote_terminal.tip_appears() && remote_terminal.status_bar_appears() {
remote_terminal.send_key(&MOVE_TAB_LEFT);
+ std::thread::sleep(std::time::Duration::from_millis(100));
step_is_complete = true;
}
step_is_complete
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index b00d1280..a3759063 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -146,9 +146,6 @@ impl InputHandler {
)) => {
self.handle_key(&key_with_modifier, raw_bytes, true);
},
- Ok((InputInstruction::SwitchToMode(input_mode), _error_context)) => {
- self.mode = input_mode;
- },
Ok((
InputInstruction::AnsiStdinInstructions(ansi_stdin_instructions),
_error_context,
@@ -180,18 +177,13 @@ impl InputHandler {
raw_bytes: Vec<u8>,
is_kitty_keyboard_protocol: bool,
) {
- let keybinds = &self.config.keybinds;
- for action in keybinds.get_actions_for_key_in_mode_or_default_action(
- &self.mode,
- key,
+ // we interpret the keys into actions on the server side so that we can change the
+ // keybinds at runtime
+ self.os_input.send_to_server(ClientToServerMsg::Key(
+ key.clone(),
raw_bytes,
is_kitty_keyboard_protocol,
- ) {
- let should_exit = self.dispatch_action(action, None);
- if should_exit {
- self.should_exit = true;
- }
- }
+ ));
}
fn handle_stdin_ansi_instruction(&mut self, ansi_stdin_instructions: AnsiStdinInstruction) {
match ansi_stdin_instructions {
@@ -316,14 +308,8 @@ impl InputHandler {
self.exit(ExitReason::NormalDetached);
should_break = true;
},
- Action::SwitchToMode(mode) => {
- // this is an optimistic update, we should get a SwitchMode instruction from the
- // server later that atomically changes the mode as well
- self.mode = mode;
- self.os_input
- .send_to_server(ClientToServerMsg::Action(action, None, None));
- },
Action::CloseFocus
+ | Action::SwitchToMode(..)
| Action::ClearScreen
| Action::NewPane(..)
| Action::Run(_)
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index e0cd195e..422e2052 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -25,7 +25,7 @@ use crate::{
use zellij_utils::{
channels::{self, ChannelWithContext, SenderWithContext},
consts::{set_permissions, ZELLIJ_SOCK_DIR},
- data::{ClientId, ConnectToSession, InputMode, KeyWithModifier, Style},
+ data::{ClientId, ConnectToSession, KeyWithModifier, Style},
envs,
errors::{ClientContext, ContextType, ErrorInstruction},
input::{config::Config, options::Options},
@@ -42,7 +42,6 @@ pub(crate) enum ClientInstruction {
Render(String),
UnblockInputThread,
Exit(ExitReason),
- SwitchToMode(InputMode),
Connected,
ActiveClients(Vec<ClientId>),
StartedParsingStdinQuery,
@@ -62,9 +61,6 @@ impl From<ServerToClientMsg> for ClientInstruction {
ServerToClientMsg::Exit(e) => ClientInstruction::Exit(e),
ServerToClientMsg::Render(buffer) => ClientInstruction::Render(buffer),
ServerToClientMsg::UnblockInputThread => ClientInstruction::UnblockInputThread,
- ServerToClientMsg::SwitchToMode(input_mode) => {
- ClientInstruction::SwitchToMode(input_mode)
- },
ServerToClientMsg::Connected => ClientInstruction::Connected,
ServerToClientMsg::ActiveClients(clients) => ClientInstruction::ActiveClients(clients),
ServerToClientMsg::Log(log_lines) => ClientInstruction::Log(log_lines),
@@ -90,7 +86,6 @@ impl From<&ClientInstruction> for ClientContext {
ClientInstruction::Error(_) => ClientContext::Error,
ClientInstruction::Render(_) => ClientContext::Render,
ClientInstruction::UnblockInputThread => ClientContext::UnblockInputThread,
- ClientInstruction::SwitchToMode(_) => ClientContext::SwitchToMode,
ClientInstruction::Connected => ClientContext::Connected,
ClientInstruction::ActiveClients(_) => ClientContext::ActiveClients,
ClientInstruction::Log(_) => ClientContext::Log,
@@ -154,7 +149,6 @@ impl ClientInfo {
pub(crate) enum InputInstruction {
KeyEvent(InputEvent, Vec<u8>),
KeyWithModifierEvent(KeyWithModifier, Vec<u8>),
- SwitchToMode(InputMode),
AnsiStdinInstructions(Vec<AnsiStdinInstruction>),
StartedParsing,
DoneParsing,
@@ -505,11 +499,6 @@ pub fn start_client(
ClientInstruction::UnblockInputThread => {
command_is_executing.unblock_input_thread();
},
- ClientInstruction::SwitchToMode(input_mode) => {
- send_input_instructions
- .send(InputInstruction::SwitchToMode(input_mode))
- .unwrap();
- },
ClientInstruction::Log(lines_to_log) => {
for line in lines_to_log {
log::info!("{line}");
@@ -634,7 +623,3 @@ pub fn start_server_detached(
os_input.connect_to_server(&*ipc_pipe);
os_input.send_to_server(first_msg);
}
-
-#[cfg(test)]
-#[path = "./unit/stdin_tests.rs"]
-mod stdin_tests;
diff --git a/zellij-client/src/unit/snapshots/zellij_client__input_handler__input_handler_tests__pixel_info_sent_to_server.snap b/zellij-client/src/unit/snapshots/zellij_client__input_handler__input_handler_tests__pixel_info_sent_to_server.snap
deleted file mode 100644
index 14e5d101..00000000
--- a/zellij-client/src/unit/snapshots/zellij_client__input_handler__input_handler_tests__pixel_info_sent_to_server.snap
+++ /dev/null
@@ -1,6 +0,0 @@
----
-source: zellij-client/src/./unit/input_handler_tests.rs
-assertion_line: 429
-expression: "*format!(\"{:?}\", events_sent_to_server.lock().unwrap())"
----
-[TerminalPixelDimensions(PixelDimensions { text_area_size: Some(SizeInPixels { height: 401, width: 1566 }), character_cell_size: None }), TerminalPixelDimensions(PixelDimensions { text_area_size: None, character_cell_size: Some(SizeInPixels { height: 21, width: 8 }) }), BackgroundColor("rgb:0000/0000/0000"), ForegroundColor("rgb:ffff/ffff/ffff"), ColorRegisters([(0, "rgb:0000/0000/0000"), (1, "rgb:cdcd/0000/0000"), (2, "rgb:0000/cdcd/0000"), (3, "rgb:cdcd/cdcd/0000"), (4, "rgb:0000/0000/eeee"), (5, "rgb:cdcd/0000/cdcd"), (6, "rgb:0000/cdcd/cdcd"), (7, "rgb:e5e5/e5e5/e5e5"), (8, "rgb:7f7f/7f7f/7f7f"), (9, "rgb:ffff/0000/0000"), (10, "rgb:0000/ffff/0000"), (11, "rgb:ffff/ffff/0000"), (12, "rgb:5c5c/5c5c/ffff"), (13, "rgb:ffff/0000/ffff"), (14, "rgb:0000/ffff/ffff"), (15, "rgb:ffff/ffff/ffff"), (16, "rgb:0000/0000/0000"), (17, "rgb:0000/0000/5f5f"), (18, "rgb:0000/0000/8787"), (19, "rgb:0000/0000/afaf"), (20, "rgb:0000/0000/d7d7"), (21, "rgb:0000/0000/ffff"), (22, "rgb:0000/5f5f/0000"), (23, "rgb:0000/5f5f/5f5f"), (24, "rgb:0000/5f5f/8787"), (25, "rgb:0000/5f5f/afaf"), (26, "rgb:0000/5f5f/d7d7"), (27, "rgb:0000/5f5f/ffff"), (28, "rgb:0000/8787/0000"), (29, "rgb:0000/8787/5f5f"), (30, "rgb:0000/8787/8787"), (31, "rgb:0000/8787/afaf"), (32, "rgb:0000/8787/d7d7"), (33, "rgb:0000/8787/ffff"), (34, "rgb:0000/afaf/0000"), (35, "rgb:0000/afaf/5f5f"), (36, "rgb:0000/afaf/8787"), (37, "rgb:0000/afaf/afaf"), (38, "rgb:0000/afaf/d7d7"), (39, "rgb:0000/afaf/ffff"), (40, "rgb:0000/d7d7/0000"), (41, "rgb:0000/d7d7/5f5f"), (42, "rgb:0000/d7d7/8787"), (43, "rgb:0000/d7d7/afaf"), (44, "rgb:0000/d7d7/d7d7"), (45, "rgb:0000/d7d7/ffff"), (46, "rgb:0000/ffff/0000"), (47, "rgb:0000/ffff/5f5f"), (48, "rgb:0000/ffff/8787"), (49, "rgb:0000/ffff/afaf"), (50, "rgb:0000/ffff/d7d7"), (51, "rgb:0000/ffff/ffff"), (52, "rgb:5f5f/0000/0000"), (53, "rgb:5f5f/0000/5f5f"), (54, "rgb:5f5f/0000/8787"), (55, "rgb:5f5f/0000/afaf"), (56, "rgb:5f5f/0000/d7d7"), (57, "rgb:5f5f/0000/ffff"), (58, "rgb:5f5f/5f5f/0000"), (59, "rgb:5f5f/5f5f/5f5f"), (60, "rgb:5f5f/5f5f/8787"), (61, "rgb:5f5f/5f5f/afaf"), (62, "rgb:5f5f/5f5f/d7d7"), (63, "rgb:5f5f/5f5f/ffff"), (64, "rgb:5f5f/8787/0000"), (65, "rgb:5f5f/8787/5f5f"), (66, "rgb:5f5f/8787/8787"), (67, "rgb:5f5f/8787/afaf"), (68, "rgb:5f5f/8787/d7d7"), (69, "rgb:5f5f/8787/ffff"), (70, "rgb:5f5f/afaf/0000"), (71, "rgb:5f5f/afaf/5f5f"), (72, "rgb:5f5f/afaf/8787"), (73, "rgb:5f5f/afaf/afaf"), (74, "rgb:5f5f/afaf/d7d7"), (75, "rgb:5f5f/afaf/ffff"), (76, "rgb:5f5f/d7d7/0000"), (77, "rgb:5f5f/d7d7/5f5f"), (78, "rgb:5f5f/d7d7/8787"), (79, "rgb:5f5f/d7d7/afaf"), (80, "rgb:5f5f/d7d7/d7d7"), (81, "rgb:5f5f/d7d7/ffff"), (82, "rgb:5f5f/ffff/0000"), (83, "rgb:5f5f/ffff/5f5f"), (84, "rgb:5f5f/ffff/8787"), (85, "rgb:5f5f/ffff/afaf"), (86, "rgb:5f5f/ffff/d7d7"), (87, "rgb:5f5f/ffff/ffff"), (88, "rgb:8787/0000/0000"), (89, "rgb:8787/0000/5f5f"), (90, "rgb:8787/0000/8787"), (91, "rgb:8787/0000/afaf"), (92, "rgb:8787/0000/d7d7"), (93, "rgb:8787/0000/ffff"), (94, "rgb:8787/5f5f/0000"), (95, "rgb:8787/5f5f/5f5f"), (96, "rgb:8787/5f5f/8787"), (97, "rgb:8787/5f5f/afaf"), (98, "rgb:8787/5f5f/d7d7"), (99, "rgb:8787/5f5f/ffff"), (100, "rgb:8787/8787/0000"), (101, "rgb:8787/8787/5f5f"), (102, "rgb:8787/8787/8787"), (103, "rgb:8787/8787/afaf"), (104, "rgb:8787/8787/d7d7"), (105, "rgb:8787/8787/ffff"), (106, "rgb:8787/afaf/0000"), (107, "rgb:8787/afaf/5f5f"), (108, "rgb:8787/afaf/8787"), (109, "rgb:8787/afaf/afaf"), (110, "rgb:8787/afaf/d7d7"), (111, "rgb:8787/afaf/ffff"), (112, "rgb:8787/d7d7/0000"), (113, "rgb:8787/d7d7/5f5f"), (114, "rgb:8787/d7d7/8787"), (115, "rgb:8787/d7d7/afaf"), (116, "rgb:8787/d7d7/d7d7"), (117, "rgb:8787/d7d7/ffff"), (118, "rgb:8787/ffff/0000"), (119, "rgb:8787/ffff/5f5f"), (120, "rgb:8787/ffff/8787"), (121, "rgb:8787/ffff/afaf"), (122, "rgb:8787/ffff/d7d7"), (123, "rgb:8787/ffff/ffff"), (124, "rgb:afaf/0000/0000"), (125, "rgb:afaf/0000/5f5f"), (126, "rgb:afaf/0000/8787"), (127, "rgb:afaf/0000/afaf"), (128, "rgb:afaf/0000/d7d7"), (129, "rgb:afaf/0000/ffff"), (130, "rgb:afaf/5f5f/0000"), (131, "rgb:afaf/5f5f/5f5f"), (132, "rgb:afaf/5f5f/8787"), (133, "rgb:afaf/5f5f/afaf"), (134, "rgb:afaf/5f5f/d7d7"), (135, "rgb:afaf/5f5f/ffff"), (136, "rgb:afaf/8787/0000"), (137, "rgb:afaf/8787/5f5f"), (138, "rgb:afaf/8787/8787"), (139, "rgb:afaf/8787/afaf"), (140, "rgb:afaf/8787/d7d7"), (141, "rgb:afaf/8787/ffff"), (142, "rgb:afaf/afaf/0000"), (143, "rgb:afaf/afaf/5f5f"), (144, "rgb:afaf/afaf/8787"), (145, "rgb:afaf/afaf/afaf"), (146, "rgb:afaf/afaf/d7d7"), (147, "rgb:afaf/afaf/ffff"), (148, "rgb:afaf/d7d7/0000"), (149, "rgb:afaf/d7d7/5f5f"), (150, "rgb:afaf/d7d7/8787"), (151, "rgb:afaf/d7d7/afaf"), (152, "rgb:afaf/d7d7/d7d7"), (153, "rgb:afaf/d7d7/ffff"), (154, "rgb:afaf/ffff/0000"), (155, "rgb:afaf/ffff/5f5f"), (156, "rgb:afaf/ffff/8787"), (157, "rgb:afaf/ffff/afaf"), (158, "rgb:afaf/ffff/d7d7"), (159, "rgb:afaf/ffff/ffff"), (160, "rgb:d7d7/0000/0000"), (161, "rgb:d7d7/0000/5f5f"), (162, "rgb:d7d7/0000/8787"), (163, "rgb:d7d7/0000/afaf"), (164, "rgb:d7d7/0000/d7d7"), (165, "rgb:d7d7/0000/ffff"), (166, "rgb:d7d7/5f5f/0000"), (167, "rgb:d7d7/5f5f/5f5f"), (168, "rgb:d7d7/5f5f/8787"), (169, "rgb:d7d7/5f5f/afaf"), (170, "rgb:d7d7/5f5f/d7d7"), (171, "rgb:d7d7/5f5f/ffff"), (172, "rgb:d7d7/8787/0000"), (173, "rgb:d7d7/8787/5f5f"), (174, "rgb:d7d7/8787/8787"), (175, "rgb:d7d7/8787/afaf"), (176, "rgb:d7d7/8787/d7d7"), (177, "rgb:d7d7/8787/ffff"), (178, "rgb:d7d7/afaf/0000"), (179, "rgb:d7d7/afaf/5f5f"), (180, "rgb:d7d7/afaf/8787"), (181, "rgb:d7d7/afaf/afaf"), (182, "rgb:d7d7/afaf/d7d7"), (183, "rgb:d7d7/afaf/ffff"), (184, "rgb:d7d7/d7d7/0000"), (185, "rgb:d7d7/d7d7/5f5f"), (186, "rgb:d7d7/d7d7/8787"), (187, "rgb:d7d7/d7d7/afaf"), (188, "rgb:d7d7/d7d7/d7d7"), (189, "rgb:d7d7/d7d7/ffff"), (190, "rgb:d7d7/ffff/0000"), (191, "rgb:d7d7/ffff/5f5f"), (192, "rgb:d7d7/ffff/8787"), (193, "rgb:d7d7/ffff/afaf"), (194, "rgb:d7d7/ffff/d7d7"), (195, "rgb:d7d7/ffff/ffff"), (196, "rgb:ffff/0000/0000"), (197, "rgb:ffff/0000/5f5f"), (198, "rgb:ffff/0000/8787"), (199, "rgb:ffff/0000/afaf"), (200, "rgb:ffff/0000/d7d7"), (201, "rgb:ffff/0000/ffff"), (202, "rgb:ffff/5f5f/0000"), (203, "rgb:ffff/5f5f/5f5f"), (204, "rgb:ffff/5f5f/8787"), (205, "rgb:ffff/5f5f/afaf"), (206, "rgb:ffff/5f5f/d7d7"), (207, "rgb:ffff/5f5f/ffff"), (208, "rgb:ffff/8787/0000"), (209, "rgb:ffff/8787/5f5f"), (210, "rgb:ffff/8787/8787"), (211, "rgb:ffff/8787/afaf"), (212, "rgb:ffff/8787/d7d7"), (213, "rgb:ffff/8787/ffff"), (214, "rgb:ffff/afaf/0000"), (215, "rgb:ffff/afaf/5f5f"), (216, "rgb:ffff/afaf/8787"), (217, "rgb:ffff/afaf/afaf"), (218, "rgb:ffff/afaf/d7d7"), (219, "rgb:ffff/afaf/ffff"), (220, "rgb:ffff/d7d7/0000"), (221, "rgb:ffff/d7d7/5f5f"), (222, "rgb:ffff/d7d7/8787"), (223, "rgb:ffff/d7d7/afaf"), (224, "rgb:ffff/d7d7/d7d7"), (225, "rgb:ffff/d7d7/ffff"), (226, "rgb:ffff/ffff/0000"), (227, "rgb:ffff/ffff/5f5f"), (228, "rgb:ffff/ffff/8787"), (229, "rgb:ffff/ffff/afaf"), (230, "rgb:ffff/ffff/d7d7"), (231, "rgb:ffff/ffff/ffff"), (232, "rgb:0808/0808/0808"), (233, "rgb:1212/1212/1212"), (234, "rgb:1c1c/1c1c/1c1c"), (235, "rgb:2626/2626/2626"), (236, "rgb:3030/3030/3030"), (237, "rgb:3a3a/3a3a/3a3a"), (238, "rgb:4444/4444/4444"), (239, "rgb:4e4e/4e4e/4e4e"), (240, "rgb:5858/5858/5858"), (241, "rgb:6262/6262/6262"), (242, "rgb:6c6c/6c6c/6c6c"), (243, "rgb:7676/7676/7676"), (244, "rgb:8080/8080/8080"), (245, "rgb:8a8a/8a8a/8a8a"), (246, "rgb:9494/9494/9494"), (247, "rgb:9e9e/9e9e/9e9e"), (248, "rgb:a8a8/a8a8/a8a8"), (249, "rgb:b2b2/b2b2/b2b2"), (250, "rgb:bcbc/bcbc/bcbc"), (251, "rgb:c6c6/c6c6/c6c6"), (252, "rgb:d0d0/d0d0/d0d0"), (253, "rgb:dada/dada/dada"), (254, "rgb:e4e4/e4e4/e4e4"), (255, "rgb:eeee/eeee/eeee")])]
diff --git a/zellij-client/src/unit/snapshots/zellij_client__stdin_tests__pixel_info_sent_to_server.snap b/zellij-client/src/unit/snapshots/zellij_client__stdin_tests__pixel_info_sent_to_server.snap
deleted file mode 100644
index c58a2f5a..00000000
--- a/zellij-client/src/unit/snapshots/zellij_client__stdin_tests__pixel_info_sent_to_server.snap
+++ /dev/null
@@ -1,6 +0,0 @@
----
-source: zellij-client/src/./unit/stdin_tests.rs
-assertion_line: 399
-expression: "*format!(\"{:?}\", events_sent_to_server.lock().unwrap())"
----
-[TerminalPixelDimensions(PixelDimensions { text_area_size: Some(SizeInPixels { height: 401, width: 1566 }), character_cell_size: None }), TerminalPixelDimensions(PixelDimensions { text_area_size: None, character_cell_size: Some(SizeInPixels { height: 21, width: 8 }) }), BackgroundColor("rgb:0000/0000/0000"), ForegroundColor("rgb:ffff/ffff/ffff"), ColorRegisters([(0, "rgb:0000/0000/0000"), (1, "rgb:cdcd/0000/0000"), (2, "rgb:0000/cdcd/0000"), (3, "rgb:cdcd/cdcd/0000"), (4, "rgb:0000/0000/eeee"), (5, "rgb:cdcd/0000/cdcd"), (6, "rgb:0000/cdcd/cdcd"), (7, "rgb:e5e5/e5e5/e5e5"), (8, "rgb:7f7f/7f7f/7f7f"), (9, "rgb:ffff/0000/0000"), (10, "rgb:0000/ffff/0000"), (11, "rgb:ffff/ffff/0000"), (12, "rgb:5c5c/5c5c/ffff"), (13, "rgb:ffff/0000/ffff"), (14, "rgb:0000/ffff/ffff"), (15, "rgb:ffff/ffff/ffff"), (16, "rgb:0000/0000/0000"), (17, "rgb:0000/0000/5f5f"), (18, "rgb:0000/0000/8787"), (19, "rgb:0000/0000/afaf"), (20, "rgb:0000/0000/d7d7"), (21, "rgb:0000/0000/ffff"), (22, "rgb:0000/5f5f/0000"), (23, "rgb:0000/5f5f/5f5f"), (24, "rgb:0000/5f5f/8787"), (25, "rgb:0000/5f5f/afaf"), (26, "rgb:0000/5f5f/d7d7"), (27, "rgb:0000/5f5f/ffff"), (28, "rgb:0000/8787/0000"), (29, "rgb:0000/8787/5f5f"), (30, "rgb:0000/8787/8787"), (31, "rgb:0000/8787/afaf"), (32, "rgb:0000/8787/d7d7"), (33, "rgb:0000/8787/ffff"), (34, "rgb:0000/afaf/0000"), (35, "rgb:0000/afaf/5f5f"), (36, "rgb:0000/afaf/8787"), (37, "rgb:0000/afaf/afaf"), (38, "rgb:0000/afaf/d7d7"), (39, "rgb:0000/afaf/ffff"), (40, "rgb:0000/d7d7/0000"), (41, "rgb:0000/d7d7/5f5f"), (42, "rgb:0000/d7d7/8787"), (43, "rgb:0000/d7d7/afaf"), (44, "rgb:0000/d7d7/d7d7"), (45, "rgb:0000/d7d7/ffff"), (46, "rgb:0000/ffff/0000"), (47, "rgb:0000/ffff/5f5f"), (48, "rgb:0000/ffff/8787"), (49, "rgb:0000/ffff/afaf"), (50, "rgb:0000/ffff/d7d7"), (51, "rgb:0000/ffff/ffff"), (52, "rgb:5f5f/0000/0000"), (53, "rgb:5f5f/0000/5f5f"), (54, "rgb:5f5f/0000/8787"), (55, "rgb:5f5f/0000/afaf"), (56, "rgb:5f5f/0000/d7d7"), (57, "rgb:5f5f/0000/ffff"), (58, "rgb:5f5f/5f5f/0000"), (59, "rgb:5f5f/5f5f/5f5f"), (60, "rgb:5f5f/5f5f/8787"), (61, "rgb:5f5f/5f5f/afaf"), (62, "rgb:5f5f/5f5f/d7d7"), (63, "rgb:5f5f/5f5f/ffff"), (64, "rgb:5f5f/8787/0000"), (65, "rgb:5f5f/8787/5f5f"), (66, "rgb:5f5f/8787/8787"), (67, "rgb:5f5f/8787/afaf"), (68, "rgb:5f5f/8787/d7d7"), (69, "rgb:5f5f/8787/ffff"), (70, "rgb:5f5f/afaf/0000"), (71, "rgb:5f5f/afaf/5f5f"), (72, "rgb:5f5f/afaf/8787"), (73, "rgb:5f5f/afaf/afaf"), (74, "rgb:5f5f/afaf/d7d7"), (75, "rgb:5f5f/afaf/ffff"), (76, "rgb:5f5f/d7d7/0000"), (77, "rgb:5f5f/d7d7/5f5f"), (78, "rgb:5f5f/d7d7/8787"), (79, "rgb:5f5f/d7d7/afaf"), (80, "rgb:5f5f/d7d7/d7d7"), (81, "rgb:5f5f/d7d7/ffff"), (82, "rgb:5f5f/ffff/0000"), (83, "rgb:5f5f/ffff/5f5f"), (84, "rgb:5f5f/ffff/8787"), (85, "rgb:5f5f/ffff/afaf"), (86, "rgb:5f5f/ffff/d7d7"), (87, "rgb:5f5f/ffff/ffff"), (88, "rgb:8787/0000/0000"), (89, "rgb:8787/0000/5f5f"), (90, "rgb:8787/0000/8787"), (91, "rgb:8787/0000/afaf"), (92, "rgb:8787/0000/d7d7"), (93, "rgb:8787/0000/ffff"), (94, "rgb:8787/5f5f/0000"), (95, "rgb:8787/5f5f/5f5f"), (96, "rgb:8787/5f5f/8787"), (97, "rgb:8787/5f5f/afaf"), (98, "rgb:8787/5f5f/d7d7"), (99, "rgb:8787/5f5f/ffff"), (100, "rgb:8787/8787/0000"), (101, "rgb:8787/8787/5f5f"), (102, "rgb:8787/8787/8787"), (103, "rgb:8787/8787/afaf"), (104, "rgb:8787/8787/d7d7"), (105, "rgb:8787/8787/ffff"), (106, "rgb:8787/afaf/0000"), (107, "rgb:8787/afaf/5f5f"), (108, "rgb:8787/afaf/8787"), (109, "rgb:8787/afaf/afaf"), (110, "rgb:8787/afaf/d7d7"), (111, "rgb:8787/afaf/ffff"), (112, "rgb:8787/d7d7/0000"), (113, "rgb:8787/d7d7/5f5f"), (114, "rgb:8787/d7d7/8787"), (115, "rgb:8787/d7d7/afaf"), (116, "rgb:8787/d7d7/d7d7"), (117, "rgb:8787/d7d7/ffff"), (118, "rgb:8787/ffff/0000"), (119, "rgb:8787/ffff/5f5f"), (120, "rgb:8787/ffff/8787"), (121, "rgb:8787/ffff/afaf"), (122, "rgb:8787/ffff/d7d7"), (123, "rgb:8787/ffff/ffff"), (124, "rgb:afaf/0000/0000"), (125, "rgb:afaf/0000/5f5f"), (126, "rgb:afaf/0000/8787"), (127, "rgb:afaf/0000/afaf"), (128, "rgb:afaf/0000/d7d7"), (129, "rgb:afaf/0000/ffff"), (130, "rgb:afaf/5f5f/0000"), (131, "rgb:afaf/5f5f/5f5f"), (132, "rgb:afaf/5f5f/8787"), (133, "rgb:afaf/5f5f/afaf"), (134, "rgb:afaf/5f5f/d7d7"), (135, "rgb:afaf/5f5f/ffff"), (136, "rgb:afaf/8787/0000"), (137, "rgb:afaf/8787/5f5f"), (138, "rgb:afaf/8787/8787"), (139, "rgb:afaf/8787/afaf"), (140, "rgb:afaf/8787/d7d7"), (141, "rgb:afaf/8787/ffff"), (142, "rgb:afaf/afaf/0000"), (143, "rgb:afaf/afaf/5f5f"), (144, "rgb:afaf/afaf/8787"), (145, "rgb:afaf/afaf/afaf"), (146, "rgb:afaf/afaf/d7d7"), (147, "rgb:afaf/afaf/ffff"), (148, "rgb:afaf/d7d7/0000"), (149, "rgb:afaf/d7d7/5f5f"), (150, "rgb:afaf/d7d7/8787"), (151, "rgb:afaf/d7d7/afaf"), (152, "rgb:afaf/d7d7/d7d7"), (153, "rgb:afaf/d7d7/ffff"), (154, "rgb:afaf/ffff/0000"), (155, "rgb:afaf/ffff/5f5f"), (156, "rgb:afaf/ffff/8787"), (157, "rgb:afaf/ffff/afaf"), (158, "rgb:afaf/ffff/d7d7"), (159, "rgb:afaf/ffff/ffff"), (160, "rgb:d7d7/0000/0000"), (161, "rgb:d7d7/0000/5f5f"), (162, "rgb:d7d7/0000/8787"), (163, "rgb:d7d7/0000/afaf"), (164, "rgb:d7d7/0000/d7d7"), (165, "rgb:d7d7/0000/ffff"), (166, "rgb:d7d7/5f5f/0000"), (167, "rgb:d7d7/5f5f/5f5f"), (168, "rgb:d7d7/5f5f/8787"), (169, "rgb:d7d7/5f5f/afaf"), (170, "rgb:d7d7/5f5f/d7d7"), (171, "rgb:d7d7/5f5f/ffff"), (172, "rgb:d7d7/8787/0000"), (173, "rgb:d7d7/8787/5f5f"), (174, "rgb:d7d7/8787/8787"), (175, "rgb:d7d7/8787/afaf"), (176, "rgb:d7d7/8787/d7d7"), (177, "rgb:d7d7/8787/ffff"), (178, "rgb:d7d7/afaf/0000"), (179, "rgb:d7d7/afaf/5f5f"), (180, "rgb:d7d7/afaf/8787"), (181, "rgb:d7d7/afaf/afaf"), (182, "rgb:d7d7/afaf/d7d7"), (183, "rgb:d7d7/afaf/ffff"), (184, "rgb:d7d7/d7d7/0000"), (185, "rgb:d7d7/d7d7/5f5f"), (186, "rgb:d7d7/d7d7/8787"), (187, "rgb:d7d7/d7d7/afaf"), (188, "rgb:d7d7/d7d7/d7d7"), (189, "rgb:d7d7/d7d7/ffff"), (190, "rgb:d7d7/ffff/0000"), (191, "rgb:d7d7/ffff/5f5f"), (192, "rgb:d7d7/ffff/8787"), (193, "rgb:d7d7/ffff/afaf"), (194, "rgb:d7d7/ffff/d7d7"), (195, "rgb:d7d7/ffff/ffff"), (196, "rgb:ffff/0000/0000"), (197, "rgb:ffff/0000/5f5f"), (198, "rgb:ffff/0000/8787"), (199, "rgb:ffff/0000/afaf"), (200, "rgb:ffff/0000/d7d7"), (201, "rgb:ffff/0000/ffff"), (202, "rgb:ffff/5f5f/0000"), (203, "rgb:ffff/5f5f/5f5f"), (204, "rgb:ffff/5f5f/8787"), (205, "rgb:ffff/5f5f/afaf"), (206, "rgb:ffff/5f5f/d7d7"), (207, "rgb:ffff/5f5f/ffff"), (208, "rgb:ffff/8787/0000"), (209, "rgb:ffff/8787/5f5f"), (210, "rgb:ffff/8787/8787"), (211, "rgb:ffff/8787/afaf"), (212, "rgb:ffff/8787/d7d7"), (213, "rgb:ffff/8787/ffff"), (214, "rgb:ffff/afaf/0000"), (215, "rgb:ffff/afaf/5f5f"), (216, "rgb:ffff/afaf/8787"), (217, "rgb:ffff/afaf/afaf"), (218, "rgb:ffff/afaf/d7d7"), (219, "rgb:ffff/afaf/ffff"), (220, "rgb:ffff/d7d7/0000"), (221, "rgb:ffff/d7d7/5f5f"), (222, "rgb:ffff/d7d7/8787"), (223, "rgb:ffff/d7d7/afaf"), (224, "rgb:ffff/d7d7/d7d7"), (225, "rgb:ffff/d7d7/ffff"), (226, "rgb:ffff/ffff/0000"), (227, "rgb:ffff/ffff/5f5f"), (228, "rgb:ffff/ffff/8787"), (229, "rgb:ffff/ffff/afaf"), (230, "rgb:ffff/ffff/d7d7"), (231, "rgb:ffff/ffff/ffff"), (232, "rgb:0808/0808/0808"), (233, "rgb:1212/1212/1212"), (234, "rgb:1c1c/1c1c/1c1c"), (235, "rgb:2626/2626/2626"), (236, "rgb:3030/3030/3030"), (237, "rgb:3a3a/3a3a/3a3a"), (238, "rgb:4444/4444/4444"), (239, "rgb:4e4e/4e4e/4e4e"), (240, "rgb:5858/5858/5858"), (241, "rgb:6262/6262/6262"), (242, "rgb:6c6c/6c6c/6c6c"), (243, "rgb:7676/7676/7676"), (244, "rgb:8080/8080/8080"), (245, "rgb:8a8a/8a8a/8a8a"), (246, "rgb:9494/9494/9494"), (247, "rgb:9e9e/9e9e/9e9e"), (248, "rgb:a8a8/a8a8/a8a8"), (249, "rgb:b2b2/b2b2/b2b2"), (250, "rgb:bcbc/bcbc/bcbc"), (251, "rgb:c6c6/c6c6/c6c6"), (252, "rgb:d0d0/d0d0/d0d0"), (253, "rgb:dada/dada/dada"), (254, "rgb:e4e4/e4e4/e4e4"), (255, "rgb:eeee/eeee/eeee")])]
diff --git a/zellij-client/src/unit/stdin_tests.rs b/zellij-client/src/unit/stdin_tests.rs
deleted file mode 100644
index 8e104887..00000000
--- a/zellij-client/src/unit/stdin_tests.rs
+++ /dev/null
@@ -1,320 +0,0 @@
-use super::input_loop;
-use crate::stdin_ansi_parser::StdinAnsiParser;
-use crate::stdin_loop;
-use zellij_utils::anyhow::Result;
-use zellij_utils::data::{Direction, InputMode, Palette};
-use zellij_utils::input::actions::Action;
-use zellij_utils::input::config::Config;
-use zellij_utils::input::options::Options;
-use zellij_utils::nix;
-use zellij_utils::pane_size::Size;
-use zellij_utils::termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers};
-
-use crate::InputInstruction;
-use crate::{
- os_input_output::{ClientOsApi, StdinPoller},
- ClientInstruction, CommandIsExecuting,
-};
-
-use ::insta::assert_snapshot;
-use std::path::Path;
-
-use std::io;
-use std::os::unix::io::RawFd;
-use std::sync::{Arc, Mutex};
-use std::thread;
-use zellij_utils::{
- errors::ErrorContext,
- ipc::{ClientToServerMsg, ServerToClientMsg},
-};
-
-use zellij_utils::channels::{self, ChannelWithContext, SenderWithContext};
-
-fn read_fixture(fixture_name: &str) -> Vec<u8> {
- let mut path_to_file = std::path::PathBuf::new();
- path_to_file.push("../src");
- path_to_file.push("tests");
- path_to_file.push("fixtures");
- path_to_file.push(fixture_name);
- std::fs::read(path_to_file)
- .unwrap_or_else(|_| panic!("could not read fixture {:?}", &fixture_name))
-}
-
-#[allow(unused)]
-pub mod commands {
- pub const QUIT: [u8; 1] = [17]; // ctrl-q
- pub const ESC: [u8; 1] = [27];
- pub const ENTER: [u8; 1] = [10]; // char '\n'
-
- pub const MOVE_FOCUS_LEFT_IN_NORMAL_MODE: [u8; 2] = [27, 104]; // alt-h
- pub const MOVE_FOCUS_RIGHT_IN_NORMAL_MODE: [u8; 2] = [27, 108]; // alt-l
-
- pub const PANE_MODE: [u8; 1] = [16]; // ctrl-p
- pub const SPAWN_TERMINAL_IN_PANE_MODE: [u8; 1] = [110]; // n
- pub const MOVE_FOCUS_IN_PANE_MODE: [u8; 1] = [112]; // p
- pub const SPLIT_DOWN_IN_PANE_MODE: [u8; 1] = [100]; // d
- pub const SPLIT_RIGHT_IN_PANE_MODE: [u8; 1] = [114]; // r
- pub const TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE: [u8; 1] = [102]; // f
- pub const CLOSE_PANE_IN_PANE_MODE: [u8; 1] = [120]; // x
- pub const MOVE_FOCUS_DOWN_IN_PANE_MODE: [u8; 1] = [106]; // j
- pub const MOVE_FOCUS_UP_IN_PANE_MODE: [u8; 1] = [107]; // k
- pub const MOVE_FOCUS_LEFT_IN_PANE_MODE: [u8; 1] = [104]; // h
- pub const MOVE_FOCUS_RIGHT_IN_PANE_MODE: [u8; 1] = [108]; // l
-
- pub const SCROLL_MODE: [u8; 1] = [19]; // ctrl-s
- pub const SCROLL_UP_IN_SCROLL_MODE: [u8; 1] = [107]; // k
- pub const SCROLL_DOWN_IN_SCROLL_MODE: [u8; 1] = [106]; // j
- pub const SCROLL_PAGE_UP_IN_SCROLL_MODE: [u8; 1] = [2]; // ctrl-b
- pub const SCROLL_PAGE_DOWN_IN_SCROLL_MODE: [u8; 1] = [6]; // ctrl-f
-
- pub const RESIZE_MODE: [u8; 1] = [18]; // ctrl-r
- pub const RESIZE_DOWN_IN_RESIZE_MODE: [u8; 1] = [106]; // j
- pub const RESIZE_UP_IN_RESIZE_MODE: [u8; 1] = [107]; // k
- pub const RESIZE_LEFT_IN_RESIZE_MODE: [u8; 1] = [104]; // h
- pub const RESIZE_RIGHT_IN_RESIZE_MODE: [u8; 1] = [108]; // l
-
- pub const TAB_MODE: [u8; 1] = [20]; // ctrl-t
- pub const NEW_TAB_IN_TAB_MODE: [u8; 1] = [110]; // n
- pub const SWITCH_NEXT_TAB_IN_TAB_MODE: [u8; 1] = [108]; // l
- pub const SWITCH_PREV_TAB_IN_TAB_MODE: [u8; 1] = [104]; // h
- pub const CLOSE_TAB_IN_TAB_MODE: [u8; 1] = [120]; // x
-
- pub const BRACKETED_PASTE_START: [u8; 6] = [27, 91, 50, 48, 48, 126]; // \u{1b}[200~
- pub const BRACKETED_PASTE_END: [u8; 6] = [27, 91, 50, 48, 49, 126]; // \u{1b}[201
- pub const SLEEP: [u8; 0] = [];
-}
-
-#[derive(Default, Clone)]
-struct FakeStdoutWriter {
- buffer: Arc<Mutex<Vec<u8>>>,
-}
-impl FakeStdoutWriter {
- pub fn new(buffer: Arc<Mutex<Vec<u8>>>) -> Self {
- FakeStdoutWriter { buffer }
- }
-}
-impl io::Write for FakeStdoutWriter {
- fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
- self.buffer.lock().unwrap().extend_from_slice(buf);
- Ok(buf.len())
- }
- fn flush(&mut self) -> Result<(), io::Error> {
- Ok(())
- }
-}
-
-#[derive(Clone)]
-struct FakeClientOsApi {
- events_sent_to_server: Arc<Mutex<Vec<ClientToServerMsg>>>,
- command_is_executing: Arc<Mutex<CommandIsExecuting>>,
- stdout_buffer: Arc<Mutex<Vec<u8>>>,
- stdin_buffer: Vec<u8>,
-}
-
-impl FakeClientOsApi {
- pub fn new(
- events_sent_to_server: Arc<Mutex<Vec<ClientToServerMsg>>>,
- command_is_executing: CommandIsExecuting,
- ) -> Self {
- // while command_is_executing itself is implemented with an Arc<Mutex>, we have to have an
- // Arc<Mutex> here because we need interior mutability, otherwise we'll have to change the
- // ClientOsApi trait, and that will cause a lot of havoc
- let command_is_executing = Arc::new(Mutex::new(command_is_executing));
- let stdout_buffer = Arc::new(Mutex::new(vec![]));
- FakeClientOsApi {
- events_sent_to_server,
- command_is_executing,
- stdout_buffer,
- stdin_buffer: vec![],
- }
- }
- pub fn with_stdin_buffer(mut self, stdin_buffer: Vec<u8>) -> Self {
- self.stdin_buffer = stdin_buffer;
- self
- }
- pub fn stdout_buffer(&self) -> Vec<u8> {
- self.stdout_buffer.lock().unwrap().drain(..).collect()
- }
-}
-
-impl ClientOsApi for FakeClientOsApi {
- fn get_terminal_size_using_fd(&self, _fd: RawFd) -> Size {
- unimplemented!()
- }
- fn set_raw_mode(&mut self, _fd: RawFd) {
- unimplemented!()
- }
- fn unset_raw_mode(&self, _fd: RawFd) -> Result<(), nix::Error> {
- unimplemented!()
- }
- fn get_stdout_writer(&self) -> Box<dyn io::Write> {
- let fake_stdout_writer = FakeStdoutWriter::new(self.stdout_buffer.clone());
- Box::new(fake_stdout_writer)
- }
- fn get_stdin_reader(&self) -> Box<dyn io::BufRead> {
- unimplemented!()
- }
- fn update_session_name(&mut self, _new_session_name: String) {}
- fn read_from_stdin(&mut self) -> Result<Vec<u8>, &'static str> {
- Ok(self.stdin_buffer.drain(..).collect())
- }
- fn box_clone(&self) -> Box<dyn ClientOsApi> {
- unimplemented!()
- }
- fn send_to_server(&self, msg: ClientToServerMsg) {
- {
- let mut events_sent_to_server = self.events_sent_to_server.lock().unwrap();
- events_sent_to_server.push(msg);
- }
- {
- let mut command_is_executing = self.command_is_executing.lock().unwrap();
- command_is_executing.unblock_input_thread();
- }
- }
- fn recv_from_server(&self) -> Option<(ServerToClientMsg, ErrorContext)> {
- unimplemented!()
- }
- fn handle_signals(&self, _sigwinch_cb: Box<dyn Fn()>, _quit_cb: Box<dyn Fn()>) {
- unimplemented!()
- }
- fn connect_to_server(&self, _path: &Path) {
- unimplemented!()
- }
- fn load_palette(&self) -> Palette {
- unimplemented!()
- }
- fn enable_mouse(&self) -> Result<()> {
- Ok(())
- }
- fn disable_mouse(&self) -> Result<()> {
- Ok(())
- }
- fn stdin_poller(&self) -> StdinPoller {
- unimplemented!()
- }
-}
-
-fn extract_actions_sent_to_server(
- events_sent_to_server: Arc<Mutex<Vec<ClientToServerMsg>>>,
-) -> Vec<Action> {
- let events_sent_to_server = events_sent_to_server.lock().unwrap();
- events_sent_to_server.iter().fold(vec![], |mut acc, event| {
- if let ClientToServerMsg::Action(action, None, None) = event {
- acc.push(action.clone());
- }
- acc
- })
-}
-
-#[test]
-pub fn quit_breaks_input_loop() {
- let stdin_events = vec![(
- commands::QUIT.to_vec(),
- InputEvent::Key(KeyEvent {
- key: KeyCode::Char('q'),
- modifiers: Modifiers::CTRL,
- }),
- )];
- let events_sent_to_server = Arc::new(Mutex::new(vec![]));
- let command_is_executing = CommandIsExecuting::new();
- let client_os_api = Box::new(FakeClientOsApi::new(
- events_sent_to_server.clone(),
- command_is_executing.clone(),
- ));
- let config = Config::from_default_assets().unwrap();
- let options = Options::default();
-
- 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);
- for event in stdin_events {
- send_input_instructions
- .send(InputInstruction::KeyEvent(event.1, event.0))
- .unwrap();
- }
-
- let default_mode = InputMode::Normal;
- input_loop(
- client_os_api,
- config,
- options,
- command_is_executing,
- send_client_instructions,
- default_mode,
- receive_input_instructions,
- );
- let expected_actions_sent_to_server = vec![Action::Quit];
- let received_actions = extract_actions_sent_to_server(events_sent_to_server);
- assert_eq!(
- expected_actions_sent_to_server, received_actions,
- "All actions sent to server properly"
- );
-}
-
-#[test]
-pub fn move_focus_left_in_normal_mode() {
- let stdin_events = vec![
- (
- commands::MOVE_FOCUS_LEFT_IN_NORMAL_MODE.to_vec(),
- InputEvent::Key(KeyEvent {
- key: KeyCode::Char('h'),
- modifiers: Modifiers::ALT,
- }),
- ),
- (
- commands::QUIT.to_vec(),
- InputEvent::Key(KeyEvent {
- key: KeyCode::Char('q'),
- modifiers: Modifiers::CTRL,
- }),
- ),
- ];
-
- let events_sent_to_server = Arc::new(Mutex::new(vec![]));
- let command_is_executing = CommandIsExecuting::new();
- let client_os_api = Box::new(FakeClientOsApi::new(
- events_sent_to_server.clone(),
- command_is_executing.clone(),
- ));
- let config = Config::from_default_assets().unwrap();
- let options = Options::default();
-
- 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);
- for event in stdin_events {
- send_input_instructions
- .send(InputInstruction::KeyEvent(event.1, event.0))
- .unwrap();
- }
-
- let default_mode = InputMode::Normal;
- input_loop(
- client_os_api,
- config,
- options,
- command_is_executing,
- send_client_instructions,
- default_mode,
- receive_input_instructions,
- );
- let expected_actions_sent_to_server =
- vec![Action::MoveFocusOrTab(Direction::Left), Action::Quit];
- let received_actions = extract_actions_sent_to_server(events_sent_to_server);
- assert_eq!(
- expected_actions_sent_to_server, received_actions,
- "All actions sent to server properly"
- );
-}
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index d33be7dd..8a264dc8 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -42,12 +42,13 @@ use zellij_utils::{
channels::{self, ChannelWithContext, SenderWithContext},
cli::CliArgs,
consts::{DEFAULT_SCROLL_BUFFER_SIZE, SCROLL_BUFFER_SIZE},
- data::{ConnectToSession, Event, PluginCapabilities},
+ data::{ConnectToSession, Event, InputMode, PluginCapabilities},
errors::{prelude::*, ContextType, ErrorInstruction, FatalError, ServerContext},
home::{default_layout_dir, get_default_data_dir},
input::{
command::{RunCommand, TerminalAction},
get_mode_info,
+ keybinds::Keybinds,
layout::Layout,
options::Options,
plugins::PluginAliases,
@@ -94,6 +95,9 @@ pub enum ServerInstruction {
client_id: ClientId,
},
DisconnectAllClientsExcept(ClientId),
+ ChangeMode(ClientId, InputMode),
+ ChangeModeForAllClients(InputMode),
+ RebindKeys(ClientId, String), // String -> stringified keybindings
}
impl From<&ServerInstruction> for ServerContext {
@@ -121,6 +125,11 @@ impl From<&ServerInstruction> for ServerContext {
ServerInstruction::DisconnectAllClientsExcept(..) => {
ServerContext::DisconnectAllClientsExcept
},
+ ServerInstruction::ChangeMode(..) => ServerContext::ChangeMode,
+ ServerInstruction::ChangeModeForAllClients(..) => {
+ ServerContext::ChangeModeForAllClients
+ },
+ ServerInstruction::RebindKeys(..) => ServerContext::RebindKeys,
}
}
}
@@ -138,6 +147,8 @@ pub(crate) struct SessionMetaData {
pub default_shell: Option<TerminalAction>,
pub layout: Box<Layout>,
pub config_options: Box<Options>,
+ pub client_keybinds: HashMap<ClientId, Keybinds>,
+ pub client_input_modes: HashMap<ClientId, InputMode>,
screen_thread: Option<thread::JoinHandle<()>>,
pty_thread: Option<thread::JoinHandle<()>>,
plugin_thread: Option<thread::JoinHandle<()>>,
@@ -145,6 +156,56 @@ pub(crate) struct SessionMetaData {
background_jobs_thread: Option<thread::JoinHandle<()>>,
}
+impl SessionMetaData {
+ pub fn set_client_keybinds(&mut self, client_id: ClientId, keybinds: Keybinds) {
+ self.client_keybinds.insert(client_id, keybinds);
+ self.client_input_modes.insert(
+ client_id,
+ self.config_options.default_mode.unwrap_or_default(),
+ );
+ }
+ pub fn get_client_keybinds_and_mode(
+ &self,
+ client_id: &ClientId,
+ ) -> Option<(&Keybinds, &InputMode)> {
+ match (
+ self.client_keybinds.get(client_id),
+ self.client_input_modes.get(client_id),
+ ) {
+ (Some(client_keybinds), Some(client_input_mode)) => {
+ Some((client_keybinds, client_input_mode))
+ },
+ _ => None,
+ }
+ }
+ pub fn change_mode_for_all_clients(&mut self, input_mode: InputMode) {
+ let all_clients: Vec<ClientId> = self.client_input_modes.keys().copied().collect();
+ for client_id in all_clients {
+ self.client_input_modes.insert(client_id, input_mode);
+ }
+ }
+ pub fn rebind_keys(&mut self, client_id: ClientId, new_keybinds: String) -> Option<Keybinds> {
+ if let Some(current_keybinds) = self.client_keybinds.get_mut(&client_id) {
+ match Keybinds::from_string(
+ new_keybinds,
+ current_keybinds.clone(),
+ &self.config_options,
+ ) {
+ Ok(new_keybinds) => {
+ *current_keybinds = new_keybinds.clone();
+ return Some(new_keybinds);
+ },
+ Err(e) => {
+ log::error!("Failed to parse keybindings: {}", e);
+ },
+ }
+ } else {
+ log::error!("Failed to bind keys for client: {client_id}");
+ }
+ None
+ }
+}
+
impl Drop for SessionMetaData {
fn drop(&mut self) {
let _ = self.senders.send_to_pty(PtyInstruction::Exit);
@@ -383,6 +444,12 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
plugin_aliases,
);
*session_data.write().unwrap() = Some(session);
+ session_data
+ .write()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .set_client_keybinds(client_id, client_attributes.keybinds.clone());
session_state
.write()
.unwrap()
@@ -469,8 +536,9 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
pane_id_to_focus,
client_id,
) => {
- let rlock = session_data.read().unwrap();
- let session_data = rlock.as_ref().unwrap();
+ let mut rlock = session_data.write().unwrap();
+ let session_data = rlock.as_mut().unwrap();
+ session_data.set_client_keybinds(client_id, attrs.keybinds.clone());
session_state
.write()
.unwrap()
@@ -498,7 +566,6 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.unwrap();
let default_mode = options.default_mode.unwrap_or_default();
let mode_info = get_mode_info(default_mode, &attrs, session_data.capabilities);
- let mode = mode_info.mode;
session_data
.senders
.send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone(), client_id))
@@ -511,12 +578,6 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
Event::ModeUpdate(mode_info),
)]))
.unwrap();
- send_to_client!(
- client_id,
- os_input,
- ServerToClientMsg::SwitchToMode(mode),
- session_state
- );
},
ServerInstruction::UnblockInputThread => {
let client_ids = session_state.read().unwrap().client_ids();
@@ -827,6 +888,42 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.unwrap()
.associate_pipe_with_client(pipe_id, client_id);
},
+ ServerInstruction::ChangeMode(client_id, input_mode) => {
+ session_data
+ .write()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .client_input_modes
+ .insert(client_id, input_mode);
+ },
+ ServerInstruction::ChangeModeForAllClients(input_mode) => {
+ session_data
+ .write()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .change_mode_for_all_clients(input_mode);
+ },
+ ServerInstruction::RebindKeys(client_id, new_keybinds) => {
+ let new_keybinds = session_data
+ .write()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .rebind_keys(client_id, new_keybinds)
+ .clone();
+ if let Some(new_keybinds) = new_keybinds {
+ session_data
+ .write()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .senders
+ .send_to_screen(ScreenInstruction::RebindKeys(new_keybinds, client_id))
+ .unwrap();
+ }
+ },
}
}
@@ -1054,6 +1151,8 @@ fn init_session(
client_attributes,
layout,
config_options: config_options.clone(),
+ client_keybinds: HashMap::new(),
+ client_input_modes: HashMap::new(),
screen_thread: Some(screen_thread),
pty_thread: Some(pty_thread),
plugin_thread: Some(plugin_thread),
diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs
index 22a1bf1f..b644fe2f 100644
--- a/zellij-server/src/plugins/unit/plugin_tests.rs
+++ b/zellij-server/src/plugins/unit/plugin_tests.rs
@@ -6487,6 +6487,87 @@ pub fn disconnect_other_clients_plugins_command() {
#[test]
#[ignore]
+pub fn rebind_keys_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, server_receiver, screen_receiver, teardown) =
+ create_plugin_thread_with_server_receiver(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+ let received_server_instruction = Arc::new(Mutex::new(vec![]));
+ let server_thread = log_actions_in_thread!(
+ received_server_instruction,
+ ServerInstruction::RebindKeys,
+ server_receiver,
+ 1
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ teardown();
+ server_thread.join().unwrap(); // this might take a while if the cache is cold
+ let rebind_keys_event = received_server_instruction
+ .lock()
+ .unwrap()
+ .iter()
+ .rev()
+ .find_map(|i| {
+ if let ServerInstruction::RebindKeys(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", rebind_keys_event));
+}
+
+#[test]
+#[ignore]
pub fn run_plugin_in_specific_cwd() {
// note that this test might sometimes fail when run alone without the rest of the suite due to
// timing issues
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
index 9c3ecd06..465f1088 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 5307
+assertion_line: 5500
expression: "format!(\"{:#?}\", permissions)"
---
Some(
@@ -14,5 +14,6 @@ Some(
WebAccess,
ReadCliPipes,
MessageAndLaunchOtherPlugins,
+ RebindKeys,
],
)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rebind_keys_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rebind_keys_plugin_command.snap
new file mode 100644
index 00000000..b5375e96
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rebind_keys_plugin_command.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 6566
+expression: "format!(\"{:#?}\", rebind_keys_event)"
+---
+Some(
+ RebindKeys(
+ 1,
+ "\n keybinds {\n locked {\n bind \"a\" { NewTab; }\n }\n }\n ",
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
index 3d9f7cfe..a0fec410 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 5217
+assertion_line: 5409
expression: "format!(\"{:#?}\", new_tab_event)"
---
Some(
@@ -16,5 +16,6 @@ Some(
WebAccess,
ReadCliPipes,
MessageAndLaunchOtherPlugins,
+ RebindKeys,
],
)
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index 8ec4064f..52ba0953 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -268,6 +268,7 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
PluginCommand::WatchFilesystem => watch_filesystem(env),
PluginCommand::DumpSessionLayout => dump_session_layout(env),
PluginCommand::CloseSelf => close_self(env),
+ PluginCommand::RebindKeys(new_keybinds) => rebind_keys(env, new_keybinds)?,
},
(PermissionStatus::Denied, permission) => {
log::error!(
@@ -842,6 +843,16 @@ fn close_self(env: &ForeignFunctionEnv) {
.non_fatal();
}
+fn rebind_keys(env: &ForeignFunctionEnv, new_keybinds: String) -> Result<()> {
+ let err_context = || "Failed to rebind keys";
+ let client_id = env.plugin_env.client_id;
+ env.plugin_env
+ .senders
+ .send_to_server(ServerInstruction::RebindKeys(client_id, new_keybinds))
+ .with_context(err_context)?;
+ Ok(())
+}
+
fn switch_to_mode(env: &ForeignFunctionEnv, input_mode: InputMode) {
let action = Action::SwitchToMode(input_mode);
let error_msg = || {
@@ -1594,6 +1605,7 @@ fn check_command_permission(
| PluginCommand::CliPipeOutput(..) => PermissionType::ReadCliPipes,
PluginCommand::MessageToPlugin(..) => PermissionType::MessageAndLaunchOtherPlugins,
PluginCommand::DumpSessionLayout => PermissionType::ReadApplicationState,
+ PluginCommand::RebindKeys(..) => PermissionType::RebindKeys,
_ => return (PermissionStatus::Granted, None),
};
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 19f11ccb..28dc4387 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -102,6 +102,9 @@ pub(crate) fn route_action(
)]))
.with_context(err_context)?;
senders
+ .send_to_server(ServerInstruction::ChangeMode(client_id, mode))
+ .with_context(err_context)?;
+ senders
.send_to_screen(ScreenInstruction::ChangeMode(
get_mode_info(mode, attrs, capabilities),
client_id,
@@ -344,6 +347,11 @@ pub(crate) fn route_action(
Event::ModeUpdate(get_mode_info(input_mode, attrs, capabilities)),
)]))
.with_context(err_context)?;
+
+ senders
+ .send_to_server(ServerInstruction::ChangeModeForAllClients(input_mode))
+ .with_context(err_context)?;
+
senders
.send_to_screen(ScreenInstruction::ChangeModeForAllClients(get_mode_info(
input_mode,
@@ -988,20 +996,42 @@ pub(crate) fn route_thread_main(
-> Result<bool> {
let mut should_break = false;
match instruction {
+ ClientToServerMsg::Key(key, raw_bytes, is_kitty_keyboard_protocol) => {
+ if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
+ match rlocked_sessions.get_client_keybinds_and_mode(&client_id) {
+ Some((keybinds, input_mode)) => {
+ for action in keybinds
+ .get_actions_for_key_in_mode_or_default_action(
+ &input_mode,
+ &key,
+ raw_bytes,
+ is_kitty_keyboard_protocol,
+ )
+ {
+ if route_action(
+ action,
+ client_id,
+ None,
+ rlocked_sessions.senders.clone(),
+ rlocked_sessions.capabilities.clone(),
+ rlocked_sessions.client_attributes.clone(),
+ rlocked_sessions.default_shell.clone(),
+ rlocked_sessions.layout.clone(),
+ Some(&mut seen_cli_pipes),
+ )? {
+ should_break = true;
+ }
+ }
+ },
+ None => {
+ log::error!("Failed to get keybindings for client");
+ },
+ }
+ }
+ },
ClientToServerMsg::Action(action, maybe_pane_id, maybe_client_id) => {
let client_id = maybe_client_id.unwrap_or(client_id);
if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
- if let Action::SwitchToMode(input_mode) = action {
- let send_res = os_input.send_to_client(
- client_id,
- ServerToClientMsg::SwitchToMode(input_mode),
- );
- if send_res.is_err() {
- let _ = to_server
- .send(ServerInstruction::RemoveClient(client_id));
- return Ok(true);
- }
- }
if route_action(
action,
client_id,
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index cf3a6dd7..8e1e4002 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -13,6 +13,7 @@ use zellij_utils::data::{
};
use zellij_utils::errors::prelude::*;
use zellij_utils::input::command::RunCommand;
+use zellij_utils::input::keybinds::Keybinds;
use zellij_utils::input::options::Clipboard;
use zellij_utils::pane_size::{Size, SizeInPixels};
use zellij_utils::{
@@ -360,6 +361,7 @@ pub enum ScreenInstruction {
DumpLayoutToHd,
RenameSession(String, ClientId), // String -> new name
ListClientsMetadata(Option<PathBuf>, ClientId), // Option<PathBuf> - default shell
+ RebindKeys(Keybinds, ClientId),
}
impl From<&ScreenInstruction> for ScreenContext {
@@ -544,6 +546,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::DumpLayoutToHd => ScreenContext::DumpLayoutToHd,
ScreenInstruction::RenameSession(..) => ScreenContext::RenameSession,
ScreenInstruction::ListClientsMetadata(..) => ScreenContext::ListClientsMetadata,
+ ScreenInstruction::RebindKeys(..) => ScreenContext::RebindKeys,
}
}
}
@@ -1774,12 +1777,6 @@ impl Screen {
tab.mark_active_pane_for_rerender(client_id);
tab.update_input_modes()?;
}
-
- if let Some(os_input) = &mut self.bus.os_input {
- let _ =
- os_input.send_to_client(client_id, ServerToClientMsg::SwitchToMode(mode_info.mode));
- }
-
Ok(())
}
pub fn change_mode_for_all_clients(&mut self, mode_info: ModeInfo) -> Result<()> {
@@ -2154,6 +2151,23 @@ impl Screen {
}
Ok(())
}
+ pub fn rebind_keys(&mut self, new_keybinds: Keybinds, client_id: ClientId) -> Result<()> {
+ if self.connected_clients_contains(&client_id) {
+ let mode_info = self
+ .mode_info
+ .entry(client_id)
+ .or_insert_with(|| self.default_mode_info.clone());
+ mode_info.update_keybinds(new_keybinds);
+ for tab in self.tabs.values_mut() {
+ tab.change_mode_info(mode_info.clone(), client_id);
+ tab.mark_active_pane_for_rerender(client_id);
+ tab.update_input_modes()?;
+ }
+ } else {
+ log::error!("Could not find client_id {client_id} to rebind keys");
+ }
+ Ok(())
+ }
fn unblock_input(&self) -> Result<()> {
self.bus
.senders
@@ -2295,6 +2309,9 @@ impl Screen {
}
found_plugin
}
+ fn connected_clients_contains(&self, client_id: &ClientId) -> bool {
+ self.connected_clients.borrow().contains(client_id)
+ }
}
// The box is here in order to make the
@@ -4010,6 +4027,9 @@ pub(crate) fn screen_thread_main(
}
screen.unblock_input()?;
},
+ ScreenInstruction::RebindKeys(new_keybinds, client_id) => {
+ screen.rebind_keys(new_keybinds, client_id).non_fatal();
+ },
}
}
Ok(())
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index bb26b5f4..1fe7af29 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -514,6 +514,8 @@ impl MockScreen {
background_jobs_thread: None,
config_options: Default::default(),
layout,
+ client_input_modes: HashMap::new(),
+ client_keybinds: HashMap::new(),
}
}
}
@@ -571,6 +573,8 @@ impl MockScreen {
background_jobs_thread: None,
config_options: Default::default(),
layout,
+ client_input_modes: HashMap::new(),
+ client_keybinds: HashMap::new(),
};
let os_input = FakeInputOutput::default();
@@ -2449,31 +2453,41 @@ pub fn send_cli_edit_action_with_split_direction() {
#[test]
pub fn send_cli_switch_mode_action() {
- let size = Size {
- cols: 121,
- rows: 20,
- };
+ let size = Size { cols: 80, rows: 10 };
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 = TiledPaneLayout::default();
initial_layout.children_split_direction = SplitDirection::Vertical;
initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::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), vec![]);
+ 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_switch_mode = CliAction::SwitchMode {
input_mode: InputMode::Locked,
};
send_cli_action_to_server(&session_metadata, cli_switch_mode, 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()
- ));
+
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ mock_screen.teardown(vec![server_instruction, screen_thread]);
+
+ let switch_mode_action = received_server_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find(|instruction| match instruction {
+ ServerInstruction::ChangeModeForAllClients(..) => true,
+ _ => false,
+ })
+ .cloned();
+
+ assert_snapshot!(format!("{:?}", switch_mode_action));
}
#[test]
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
index f79859e9..283fafef 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-server/src/./unit/screen_tests.rs
-assertion_line: 2465
-expression: "format!(\"{:?}\", *\n mock_screen.os_input.server_to_client_messages.lock().unwrap())"
+assertion_line: 2521
+expression: "format!(\"{:?}\", switch_mode_action)"
---
-{1: [QueryTerminalSize, SwitchToMode(Locked)]}
+Some(ChangeModeForAllClients(Locked))
diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs
index ead21332..01049b37 100644
--- a/zellij-tile/src/shim.rs
+++ b/zellij-tile/src/shim.rs
@@ -808,6 +808,14 @@ pub fn dump_session_layout() {
unsafe { host_run_plugin_command() };
}
+/// Rebind keys for the current user
+pub fn rebind_keys(keys: String) {
+ let plugin_command = PluginCommand::RebindKeys(keys);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
// Utility Functions
#[allow(unused)]
diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs
index 98883353..393f2e0c 100644
--- a/zellij-utils/assets/prost/api.plugin_command.rs
+++ b/zellij-utils/assets/prost/api.plugin_command.rs
@@ -5,7 +5,7 @@ pub struct PluginCommand {
pub name: i32,
#[prost(
oneof = "plugin_command::Payload",
- tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62"
+ tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63"
)]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
@@ -118,6 +118,8 @@ pub mod plugin_command {
ScanHostFolderPayload(::prost::alloc::string::String),
#[prost(message, tag = "62")]
NewTabsWithLayoutInfoPayload(super::NewTabsWithLayoutInfoPayload),
+ #[prost(string, tag = "63")]
+ RebindKeysPayload(::prost::alloc::string::String),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -431,6 +433,7 @@ pub enum CommandName {
DumpSessionLayout = 84,
CloseSelf = 85,
NewTabsWithLayoutInfo = 86,
+ RebindKeys = 87,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -526,6 +529,7 @@ impl CommandName {
CommandName::DumpSessionLayout => "DumpSessionLayout",
CommandName::CloseSelf => "CloseSelf",
CommandName::NewTabsWithLayoutInfo => "NewTabsWithLayoutInfo",
+ CommandName::RebindKeys => "RebindKeys",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -618,6 +622,7 @@ impl CommandName {
"DumpSessionLayout" => Some(Self::DumpSessionLayout),
"CloseSelf" => Some(Self::CloseSelf),
"NewTabsWithLayoutInfo" => Some(Self::NewTabsWithLayoutInfo),
+ "RebindKeys" => Some(Self::RebindKeys),
_ => None,
}
}
diff --git a/zellij-utils/assets/prost/api.plugin_permission.rs b/zellij-utils/assets/prost/api.plugin_permission.rs
index f928625d..50bc853f 100644
--- a/zellij-utils/assets/prost/api.plugin_permission.rs
+++ b/zellij-utils/assets/prost/api.plugin_permission.rs
@@ -10,6 +10,7 @@ pub enum PermissionType {
WebAccess = 6,
ReadCliPipes = 7,
MessageAndLaunchOtherPlugins = 8,
+ RebindKeys = 9,
}
impl PermissionType {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -29,6 +30,7 @@ impl PermissionType {
PermissionType::MessageAndLaunchOtherPlugins => {
"MessageAndLaunchOtherPlugins"
}
+ PermissionType::RebindKeys => "RebindKeys",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -43,6 +45,7 @@ impl PermissionType {
"WebAccess" => Some(Self::WebAccess),
"ReadCliPipes" => Some(Self::ReadCliPipes),
"MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins),
+ "RebindKeys" => Some(Self::RebindKeys),
_ => None,
}
}
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 2625b252..602b1942 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -1,5 +1,6 @@
use crate::input::actions::Action;
use crate::input::config::ConversionError;
+use crate::input::keybinds::Keybinds;
use crate::input::layout::SplitSize;
use clap::ArgEnum;
use serde::{Deserialize, Serialize};
@@ -914,6 +915,7 @@ pub enum Permission {
WebAccess,
ReadCliPipes,
MessageAndLaunchOtherPlugins,
+ RebindKeys,
}
impl PermissionType {
@@ -934,6 +936,7 @@ impl PermissionType {
PermissionType::MessageAndLaunchOtherPlugins => {
"Send messages to and launch other plugins".to_owned()
},
+ PermissionType::RebindKeys => "Rebind keys".to_owned(),
}
}
}
@@ -1130,6 +1133,9 @@ impl ModeInfo {
}
vec![]
}
+ pub fn update_keybinds(&mut self, keybinds: Keybinds) {
+ self.keybinds = keybinds.to_keybinds_vec();
+ }
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -1733,4 +1739,5 @@ pub enum PluginCommand {
DumpSessionLayout,
CloseSelf,
NewTabsWithLayoutInfo(LayoutInfo),
+ RebindKeys(String), // String -> stringified keybindings
}
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 248ff9be..3656d8ad 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -353,6 +353,7 @@ pub enum ScreenContext {
RenameSession,
DumpLayoutToPlugin,
ListClientsMetadata,
+ RebindKeys,
}
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
@@ -454,6 +455,9 @@ pub enum ServerContext {
CliPipeOutput,
AssociatePipeWithClient,
DisconnectAllClientsExcept,
+ ChangeMode,
+ ChangeModeForAllClients,
+ RebindKeys,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
diff --git a/zellij-utils/src/ipc.rs b/zellij-utils/src/ipc.rs
index fc004457..2713ca78 100644
--- a/zellij-utils/src/ipc.rs
+++ b/zellij-utils/src/ipc.rs
@@ -1,7 +1,7 @@
//! IPC stuff for starting to split things into a client and server model.
use crate::{
cli::CliArgs,
- data::{ClientId, ConnectToSession, InputMode, Style},
+ data::{ClientId, ConnectToSession, KeyWithModifier, Style},
errors::{get_current_ctx, prelude::*, ErrorContext},
input::keybinds::Keybinds,
input::{actions::Action, layout::Layout, options::Options, plugins::PluginAliases},
@@ -85,6 +85,7 @@ pub enum ClientToServerMsg {
Option<(u32, bool)>, // (pane_id, is_plugin) => pane id to focus
),
Action(Action, Option<u32>, Option<ClientId>), // u32 is the terminal id
+ Key(KeyWithModifier, Vec<u8>, bool), // key, raw_bytes, is_kitty_keyboard_protocol
ClientExited,
KillSession,
ConnStatus,
@@ -97,7 +98,6 @@ pub enum ServerToClientMsg {
Render(String),
UnblockInputThread,
Exit(ExitReason),
- SwitchToMode(InputMode),
Connected,
ActiveClients(Vec<ClientId>),
Log(Vec<String>),
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 288c09f7..a69b6b02 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1880,6 +1880,22 @@ impl Keybinds {
}
Ok(input_mode_keybinds)
}
+ pub fn from_string(
+ stringified_keybindings: String,
+ base_keybinds: Keybinds,
+ config_options: &Options,
+ ) -> Result<Self, ConfigError> {
+ let document: KdlDocument = stringified_keybindings.parse()?;
+ if let Some(kdl_keybinds) = document.get("keybinds") {
+ Keybinds::from_kdl(&kdl_keybinds, base_keybinds, config_options)
+ } else {
+ Err(ConfigError::new_kdl_error(
+ format!("Could not find keybinds node"),
+ document.span().offset(),
+ document.span().len(),
+ ))
+ }
+ }
}
impl Config {
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index 718e8b4c..14ab3045 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -98,6 +98,7 @@ enum CommandName {
DumpSessionLayout = 84;
CloseSelf = 85;
NewTabsWithLayoutInfo = 86;
+ RebindKeys = 87;
}
message PluginCommand {
@@ -155,6 +156,7 @@ message PluginCommand {
KillSessionsPayload kill_sessions_payload = 60;
string scan_host_folder_payload = 61;
NewTabsWithLayoutInfoPayload new_tabs_with_layout_info_payload = 62;
+ string rebind_keys_payload = 63;
}
}
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 771b6151..aeafc1d4 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -888,6 +888,12 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for NewTabsWithLayoutInfo"),
},
+ Some(CommandName::RebindKeys) => match protobuf_plugin_command.payload {
+ Some(Payload::RebindKeysPayload(rebind_keys_payload)) => {
+ Ok(PluginCommand::RebindKeys(rebind_keys_payload))
+ },
+ _ => Err("Mismatched payload for RebindKeys"),
+ },
None => Err("Unrecognized plugin command"),
}
}
@@ -1420,6 +1426,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
)),
})
},
+ PluginCommand::RebindKeys(rebind_keys_payload) => Ok(ProtobufPluginCommand {
+ name: CommandName::RebindKeys as i32,
+ payload: Some(Payload::RebindKeysPayload(rebind_keys_payload)),
+ }),
}
}
}
diff --git a/zellij-utils/src/plugin_api/plugin_permission.proto b/zellij-utils/src/plugin_api/plugin_permission.proto
index 761384c1..cc1fda6a 100644
--- a/zellij-utils/src/plugin_api/plugin_permission.proto
+++ b/zellij-utils/src/plugin_api/plugin_permission.proto
@@ -12,4 +12,5 @@ enum PermissionType {
WebAccess = 6;
ReadCliPipes = 7;
MessageAndLaunchOtherPlugins = 8;
+ RebindKeys = 9;
}
diff --git a/zellij-utils/src/plugin_api/plugin_permission.rs b/zellij-utils/src/plugin_api/plugin_permission.rs
index 4f258ac2..968de917 100644
--- a/zellij-utils/src/plugin_api/plugin_permission.rs
+++ b/zellij-utils/src/plugin_api/plugin_permission.rs
@@ -24,6 +24,7 @@ impl TryFrom<ProtobufPermissionType> for PermissionType {
ProtobufPermissionType::MessageAndLaunchOtherPlugins => {
Ok(PermissionType::MessageAndLaunchOtherPlugins)
},
+ ProtobufPermissionType::RebindKeys => Ok(PermissionType::RebindKeys),
}
}
}
@@ -49,6 +50,7 @@ impl TryFrom<PermissionType> for ProtobufPermissionType {
PermissionType::MessageAndLaunchOtherPlugins => {
Ok(ProtobufPermissionType::MessageAndLaunchOtherPlugins)
},
+ PermissionType::RebindKeys => Ok(ProtobufPermissionType::RebindKeys),
}
}
}