feat(keybindings): support multiple modifiers (eg. Ctrl+Alt) and the kitty keyboard protocol (#3383)
* parse kitty keys from STDIN * work * work * replace internal Key representation with the new KeyWithModifier in all the places * work * work * allow disabling with config * adjust ordering * handle enabling/disabling properly on the client * support multiple modifiers without kitty too * normalize uppercase keys * get tests to pass * various cleanups * style(fmt): rustfmt
This commit is contained in:
parent
c72f3a712b
commit
62c37a87cc
66 changed files with 21302 additions and 11902 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -5306,6 +5306,7 @@ dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
|
"bitflags 2.5.0",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"colored",
|
"colored",
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,11 @@ impl ZellijPlugin for State {
|
||||||
|
|
||||||
fn update(&mut self, event: Event) -> bool {
|
fn update(&mut self, event: Event) -> bool {
|
||||||
match &event {
|
match &event {
|
||||||
Event::Key(key) => match key {
|
Event::Key(key) => match key.bare_key {
|
||||||
Key::Char('a') => {
|
BareKey::Char('a') if key.has_no_modifiers() => {
|
||||||
switch_to_input_mode(&InputMode::Tab);
|
switch_to_input_mode(&InputMode::Tab);
|
||||||
},
|
},
|
||||||
Key::Char('b') => {
|
BareKey::Char('b') if key.has_no_modifiers() => {
|
||||||
new_tabs_with_layout(
|
new_tabs_with_layout(
|
||||||
"layout {
|
"layout {
|
||||||
tab {
|
tab {
|
||||||
|
|
@ -90,85 +90,87 @@ impl ZellijPlugin for State {
|
||||||
}",
|
}",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Char('c') => new_tab(),
|
BareKey::Char('c') if key.has_no_modifiers() => new_tab(),
|
||||||
Key::Char('d') => go_to_next_tab(),
|
BareKey::Char('d') if key.has_no_modifiers() => go_to_next_tab(),
|
||||||
Key::Char('e') => go_to_previous_tab(),
|
BareKey::Char('e') if key.has_no_modifiers() => go_to_previous_tab(),
|
||||||
Key::Char('f') => {
|
BareKey::Char('f') if key.has_no_modifiers() => {
|
||||||
let resize = Resize::Increase;
|
let resize = Resize::Increase;
|
||||||
resize_focused_pane(resize)
|
resize_focused_pane(resize)
|
||||||
},
|
},
|
||||||
Key::Char('g') => {
|
BareKey::Char('g') if key.has_no_modifiers() => {
|
||||||
let resize = Resize::Increase;
|
let resize = Resize::Increase;
|
||||||
let direction = Direction::Left;
|
let direction = Direction::Left;
|
||||||
resize_focused_pane_with_direction(resize, direction);
|
resize_focused_pane_with_direction(resize, direction);
|
||||||
},
|
},
|
||||||
Key::Char('h') => focus_next_pane(),
|
BareKey::Char('h') if key.has_no_modifiers() => focus_next_pane(),
|
||||||
Key::Char('i') => focus_previous_pane(),
|
BareKey::Char('i') if key.has_no_modifiers() => focus_previous_pane(),
|
||||||
Key::Char('j') => {
|
BareKey::Char('j') if key.has_no_modifiers() => {
|
||||||
let direction = Direction::Left;
|
let direction = Direction::Left;
|
||||||
move_focus(direction)
|
move_focus(direction)
|
||||||
},
|
},
|
||||||
Key::Char('k') => {
|
BareKey::Char('k') if key.has_no_modifiers() => {
|
||||||
let direction = Direction::Left;
|
let direction = Direction::Left;
|
||||||
move_focus_or_tab(direction)
|
move_focus_or_tab(direction)
|
||||||
},
|
},
|
||||||
Key::Char('l') => detach(),
|
BareKey::Char('l') if key.has_no_modifiers() => detach(),
|
||||||
Key::Char('m') => edit_scrollback(),
|
BareKey::Char('m') if key.has_no_modifiers() => edit_scrollback(),
|
||||||
Key::Char('n') => {
|
BareKey::Char('n') if key.has_no_modifiers() => {
|
||||||
let bytes = vec![102, 111, 111];
|
let bytes = vec![102, 111, 111];
|
||||||
write(bytes)
|
write(bytes)
|
||||||
},
|
},
|
||||||
Key::Char('o') => {
|
BareKey::Char('o') if key.has_no_modifiers() => {
|
||||||
let chars = "foo";
|
let chars = "foo";
|
||||||
write_chars(chars);
|
write_chars(chars);
|
||||||
},
|
},
|
||||||
Key::Char('p') => toggle_tab(),
|
BareKey::Char('p') if key.has_no_modifiers() => toggle_tab(),
|
||||||
Key::Char('q') => move_pane(),
|
BareKey::Char('q') if key.has_no_modifiers() => move_pane(),
|
||||||
Key::Char('r') => {
|
BareKey::Char('r') if key.has_no_modifiers() => {
|
||||||
let direction = Direction::Left;
|
let direction = Direction::Left;
|
||||||
move_pane_with_direction(direction)
|
move_pane_with_direction(direction)
|
||||||
},
|
},
|
||||||
Key::Char('s') => clear_screen(),
|
BareKey::Char('s') if key.has_no_modifiers() => clear_screen(),
|
||||||
Key::Char('t') => scroll_up(),
|
BareKey::Char('t') if key.has_no_modifiers() => scroll_up(),
|
||||||
Key::Char('u') => scroll_down(),
|
BareKey::Char('u') if key.has_no_modifiers() => scroll_down(),
|
||||||
Key::Char('v') => scroll_to_top(),
|
BareKey::Char('v') if key.has_no_modifiers() => scroll_to_top(),
|
||||||
Key::Char('w') => scroll_to_bottom(),
|
BareKey::Char('w') if key.has_no_modifiers() => scroll_to_bottom(),
|
||||||
Key::Char('x') => page_scroll_up(),
|
BareKey::Char('x') if key.has_no_modifiers() => page_scroll_up(),
|
||||||
Key::Char('y') => page_scroll_down(),
|
BareKey::Char('y') if key.has_no_modifiers() => page_scroll_down(),
|
||||||
Key::Char('z') => toggle_focus_fullscreen(),
|
BareKey::Char('z') if key.has_no_modifiers() => toggle_focus_fullscreen(),
|
||||||
Key::Char('1') => toggle_pane_frames(),
|
BareKey::Char('1') if key.has_no_modifiers() => toggle_pane_frames(),
|
||||||
Key::Char('2') => toggle_pane_embed_or_eject(),
|
BareKey::Char('2') if key.has_no_modifiers() => toggle_pane_embed_or_eject(),
|
||||||
Key::Char('3') => undo_rename_pane(),
|
BareKey::Char('3') if key.has_no_modifiers() => undo_rename_pane(),
|
||||||
Key::Char('4') => close_focus(),
|
BareKey::Char('4') if key.has_no_modifiers() => close_focus(),
|
||||||
Key::Char('5') => toggle_active_tab_sync(),
|
BareKey::Char('5') if key.has_no_modifiers() => toggle_active_tab_sync(),
|
||||||
Key::Char('6') => close_focused_tab(),
|
BareKey::Char('6') if key.has_no_modifiers() => close_focused_tab(),
|
||||||
Key::Char('7') => undo_rename_tab(),
|
BareKey::Char('7') if key.has_no_modifiers() => undo_rename_tab(),
|
||||||
Key::Char('8') => quit_zellij(),
|
BareKey::Char('8') if key.has_no_modifiers() => quit_zellij(),
|
||||||
Key::Ctrl('a') => previous_swap_layout(),
|
BareKey::Char('a') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
Key::Ctrl('b') => next_swap_layout(),
|
previous_swap_layout()
|
||||||
Key::Ctrl('c') => {
|
},
|
||||||
|
BareKey::Char('b') if key.has_modifiers(&[KeyModifier::Ctrl]) => next_swap_layout(),
|
||||||
|
BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let tab_name = "my tab name";
|
let tab_name = "my tab name";
|
||||||
go_to_tab_name(tab_name)
|
go_to_tab_name(tab_name)
|
||||||
},
|
},
|
||||||
Key::Ctrl('d') => {
|
BareKey::Char('d') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let tab_name = "my tab name";
|
let tab_name = "my tab name";
|
||||||
focus_or_create_tab(tab_name)
|
focus_or_create_tab(tab_name)
|
||||||
},
|
},
|
||||||
Key::Ctrl('e') => {
|
BareKey::Char('e') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let tab_index = 2;
|
let tab_index = 2;
|
||||||
go_to_tab(tab_index)
|
go_to_tab(tab_index)
|
||||||
},
|
},
|
||||||
Key::Ctrl('f') => {
|
BareKey::Char('f') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let plugin_url = "file:/path/to/my/plugin.wasm";
|
let plugin_url = "file:/path/to/my/plugin.wasm";
|
||||||
start_or_reload_plugin(plugin_url)
|
start_or_reload_plugin(plugin_url)
|
||||||
},
|
},
|
||||||
Key::Ctrl('g') => {
|
BareKey::Char('g') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_file(FileToOpen {
|
open_file(FileToOpen {
|
||||||
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Key::Ctrl('h') => {
|
BareKey::Char('h') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_file_floating(
|
open_file_floating(
|
||||||
FileToOpen {
|
FileToOpen {
|
||||||
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
||||||
|
|
@ -177,14 +179,14 @@ impl ZellijPlugin for State {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('i') => {
|
BareKey::Char('i') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_file(FileToOpen {
|
open_file(FileToOpen {
|
||||||
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
||||||
line_number: Some(42),
|
line_number: Some(42),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Key::Ctrl('j') => {
|
BareKey::Char('j') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_file_floating(
|
open_file_floating(
|
||||||
FileToOpen {
|
FileToOpen {
|
||||||
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
||||||
|
|
@ -194,23 +196,23 @@ impl ZellijPlugin for State {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('k') => {
|
BareKey::Char('k') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_terminal(std::path::PathBuf::from("/path/to/my/file.rs").as_path());
|
open_terminal(std::path::PathBuf::from("/path/to/my/file.rs").as_path());
|
||||||
},
|
},
|
||||||
Key::Ctrl('l') => {
|
BareKey::Char('l') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_terminal_floating(
|
open_terminal_floating(
|
||||||
std::path::PathBuf::from("/path/to/my/file.rs").as_path(),
|
std::path::PathBuf::from("/path/to/my/file.rs").as_path(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('m') => {
|
BareKey::Char('m') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_command_pane(CommandToRun {
|
open_command_pane(CommandToRun {
|
||||||
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
||||||
args: vec!["arg1".to_owned(), "arg2".to_owned()],
|
args: vec!["arg1".to_owned(), "arg2".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Key::Ctrl('n') => {
|
BareKey::Char('n') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
open_command_pane_floating(
|
open_command_pane_floating(
|
||||||
CommandToRun {
|
CommandToRun {
|
||||||
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
path: std::path::PathBuf::from("/path/to/my/file.rs"),
|
||||||
|
|
@ -220,51 +222,51 @@ impl ZellijPlugin for State {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('o') => {
|
BareKey::Char('o') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
switch_tab_to(1);
|
switch_tab_to(1);
|
||||||
},
|
},
|
||||||
Key::Ctrl('p') => {
|
BareKey::Char('p') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
hide_self();
|
hide_self();
|
||||||
},
|
},
|
||||||
Key::Ctrl('q') => {
|
BareKey::Char('q') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let should_float_if_hidden = false;
|
let should_float_if_hidden = false;
|
||||||
show_self(should_float_if_hidden);
|
show_self(should_float_if_hidden);
|
||||||
},
|
},
|
||||||
Key::Ctrl('r') => {
|
BareKey::Char('r') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
close_terminal_pane(1);
|
close_terminal_pane(1);
|
||||||
},
|
},
|
||||||
Key::Ctrl('s') => {
|
BareKey::Char('s') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
close_plugin_pane(1);
|
close_plugin_pane(1);
|
||||||
},
|
},
|
||||||
Key::Ctrl('t') => {
|
BareKey::Char('t') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let should_float_if_hidden = false;
|
let should_float_if_hidden = false;
|
||||||
focus_terminal_pane(1, should_float_if_hidden);
|
focus_terminal_pane(1, should_float_if_hidden);
|
||||||
},
|
},
|
||||||
Key::Ctrl('u') => {
|
BareKey::Char('u') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let should_float_if_hidden = false;
|
let should_float_if_hidden = false;
|
||||||
focus_plugin_pane(1, should_float_if_hidden);
|
focus_plugin_pane(1, should_float_if_hidden);
|
||||||
},
|
},
|
||||||
Key::Ctrl('v') => {
|
BareKey::Char('v') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
rename_terminal_pane(1, "new terminal_pane_name");
|
rename_terminal_pane(1, "new terminal_pane_name");
|
||||||
},
|
},
|
||||||
Key::Ctrl('w') => {
|
BareKey::Char('w') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
rename_plugin_pane(1, "new plugin_pane_name");
|
rename_plugin_pane(1, "new plugin_pane_name");
|
||||||
},
|
},
|
||||||
Key::Ctrl('x') => {
|
BareKey::Char('x') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
rename_tab(1, "new tab name");
|
rename_tab(1, "new tab name");
|
||||||
},
|
},
|
||||||
Key::Ctrl('z') => {
|
BareKey::Char('z') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
go_to_tab_name(&format!("{:?}", self.configuration));
|
go_to_tab_name(&format!("{:?}", self.configuration));
|
||||||
},
|
},
|
||||||
Key::Ctrl('1') => {
|
BareKey::Char('1') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
request_permission(&[PermissionType::ReadApplicationState]);
|
request_permission(&[PermissionType::ReadApplicationState]);
|
||||||
},
|
},
|
||||||
Key::Ctrl('2') => {
|
BareKey::Char('2') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let mut context = BTreeMap::new();
|
let mut context = BTreeMap::new();
|
||||||
context.insert("user_key_1".to_owned(), "user_value_1".to_owned());
|
context.insert("user_key_1".to_owned(), "user_value_1".to_owned());
|
||||||
run_command(&["ls", "-l"], context);
|
run_command(&["ls", "-l"], context);
|
||||||
},
|
},
|
||||||
Key::Ctrl('3') => {
|
BareKey::Char('3') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let mut context = BTreeMap::new();
|
let mut context = BTreeMap::new();
|
||||||
context.insert("user_key_2".to_owned(), "user_value_2".to_owned());
|
context.insert("user_key_2".to_owned(), "user_value_2".to_owned());
|
||||||
let mut env_vars = BTreeMap::new();
|
let mut env_vars = BTreeMap::new();
|
||||||
|
|
@ -276,7 +278,7 @@ impl ZellijPlugin for State {
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('4') => {
|
BareKey::Char('4') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let mut headers = BTreeMap::new();
|
let mut headers = BTreeMap::new();
|
||||||
let mut context = BTreeMap::new();
|
let mut context = BTreeMap::new();
|
||||||
let body = vec![1, 2, 3];
|
let body = vec![1, 2, 3];
|
||||||
|
|
@ -292,22 +294,24 @@ impl ZellijPlugin for State {
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('5') => {
|
BareKey::Char('5') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
switch_session(Some("my_new_session"));
|
switch_session(Some("my_new_session"));
|
||||||
},
|
},
|
||||||
Key::Ctrl('6') => disconnect_other_clients(),
|
BareKey::Char('6') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
Key::Ctrl('7') => {
|
disconnect_other_clients()
|
||||||
|
},
|
||||||
|
BareKey::Char('7') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
switch_session_with_layout(
|
switch_session_with_layout(
|
||||||
Some("my_other_new_session"),
|
Some("my_other_new_session"),
|
||||||
LayoutInfo::BuiltIn("compact".to_owned()),
|
LayoutInfo::BuiltIn("compact".to_owned()),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Key::Ctrl('8') => {
|
BareKey::Char('8') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
let mut file = std::fs::File::create("/host/hi-from-plugin.txt").unwrap();
|
let mut file = std::fs::File::create("/host/hi-from-plugin.txt").unwrap();
|
||||||
file.write_all(b"Hi there!").unwrap();
|
file.write_all(b"Hi there!").unwrap();
|
||||||
},
|
},
|
||||||
Key::Ctrl('9') => {
|
BareKey::Char('9') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
switch_session_with_layout(
|
switch_session_with_layout(
|
||||||
Some("my_other_new_session_with_cwd"),
|
Some("my_other_new_session_with_cwd"),
|
||||||
LayoutInfo::BuiltIn("compact".to_owned()),
|
LayoutInfo::BuiltIn("compact".to_owned()),
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ impl State {
|
||||||
fn reset_selected_index(&mut self) {
|
fn reset_selected_index(&mut self) {
|
||||||
self.sessions.reset_selected_index();
|
self.sessions.reset_selected_index();
|
||||||
}
|
}
|
||||||
fn handle_key(&mut self, key: Key) -> bool {
|
fn handle_key(&mut self, key: KeyWithModifier) -> bool {
|
||||||
if self.error.is_some() {
|
if self.error.is_some() {
|
||||||
self.error = None;
|
self.error = None;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -186,197 +186,246 @@ impl State {
|
||||||
ActiveScreen::ResurrectSession => self.handle_resurrect_session_key(key),
|
ActiveScreen::ResurrectSession => self.handle_resurrect_session_key(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn handle_new_session_key(&mut self, key: Key) -> bool {
|
fn handle_new_session_key(&mut self, key: KeyWithModifier) -> bool {
|
||||||
let mut should_render = false;
|
let mut should_render = false;
|
||||||
if let Key::Down = key {
|
match key.bare_key {
|
||||||
self.new_session_info.handle_key(key);
|
BareKey::Down if key.has_no_modifiers() => {
|
||||||
should_render = true;
|
|
||||||
} else if let Key::Up = key {
|
|
||||||
self.new_session_info.handle_key(key);
|
|
||||||
should_render = true;
|
|
||||||
} else if let Key::Char(character) = key {
|
|
||||||
if character == '\n' {
|
|
||||||
self.handle_selection();
|
|
||||||
} else {
|
|
||||||
self.new_session_info.handle_key(key);
|
self.new_session_info.handle_key(key);
|
||||||
}
|
should_render = true;
|
||||||
should_render = true;
|
},
|
||||||
} else if let Key::Backspace = key {
|
BareKey::Up if key.has_no_modifiers() => {
|
||||||
self.new_session_info.handle_key(key);
|
self.new_session_info.handle_key(key);
|
||||||
should_render = true;
|
should_render = true;
|
||||||
} else if let Key::Ctrl('w') = key {
|
},
|
||||||
self.active_screen = ActiveScreen::NewSession;
|
BareKey::Char(character) if key.has_no_modifiers() => {
|
||||||
should_render = true;
|
if character == '\n' {
|
||||||
} else if let Key::BackTab = key {
|
self.handle_selection();
|
||||||
self.toggle_active_screen();
|
} else {
|
||||||
should_render = true;
|
self.new_session_info.handle_key(key);
|
||||||
} else if let Key::Ctrl('f') = key {
|
}
|
||||||
let request_id = Uuid::new_v4();
|
should_render = true;
|
||||||
let mut config = BTreeMap::new();
|
},
|
||||||
let mut args = BTreeMap::new();
|
BareKey::Backspace if key.has_no_modifiers() => {
|
||||||
self.request_ids.push(request_id.to_string());
|
self.new_session_info.handle_key(key);
|
||||||
// we insert this into the config so that a new plugin will be opened (the plugin's
|
should_render = true;
|
||||||
// uniqueness is determined by its name/url as well as its config)
|
},
|
||||||
config.insert("request_id".to_owned(), request_id.to_string());
|
BareKey::Char('w') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
// we also insert this into the args so that the plugin will have an easier access to
|
self.active_screen = ActiveScreen::NewSession;
|
||||||
// it
|
should_render = true;
|
||||||
args.insert("request_id".to_owned(), request_id.to_string());
|
},
|
||||||
pipe_message_to_plugin(
|
BareKey::Tab if key.has_no_modifiers() => {
|
||||||
MessageToPlugin::new("filepicker")
|
self.toggle_active_screen();
|
||||||
.with_plugin_url("filepicker")
|
should_render = true;
|
||||||
.with_plugin_config(config)
|
},
|
||||||
.new_plugin_instance_should_have_pane_title(
|
BareKey::Char('f') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
"Select folder for the new session...",
|
let request_id = Uuid::new_v4();
|
||||||
)
|
let mut config = BTreeMap::new();
|
||||||
.with_args(args),
|
let mut args = BTreeMap::new();
|
||||||
);
|
self.request_ids.push(request_id.to_string());
|
||||||
should_render = true;
|
// we insert this into the config so that a new plugin will be opened (the plugin's
|
||||||
} else if let Key::Ctrl('c') = key {
|
// uniqueness is determined by its name/url as well as its config)
|
||||||
self.new_session_info.new_session_folder = None;
|
config.insert("request_id".to_owned(), request_id.to_string());
|
||||||
should_render = true;
|
// we also insert this into the args so that the plugin will have an easier access to
|
||||||
} else if let Key::Esc = key {
|
// it
|
||||||
self.new_session_info.handle_key(key);
|
args.insert("request_id".to_owned(), request_id.to_string());
|
||||||
should_render = true;
|
pipe_message_to_plugin(
|
||||||
|
MessageToPlugin::new("filepicker")
|
||||||
|
.with_plugin_url("filepicker")
|
||||||
|
.with_plugin_config(config)
|
||||||
|
.new_plugin_instance_should_have_pane_title(
|
||||||
|
"Select folder for the new session...",
|
||||||
|
)
|
||||||
|
.with_args(args),
|
||||||
|
);
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
|
self.new_session_info.new_session_folder = None;
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Esc if key.has_no_modifiers() => {
|
||||||
|
self.new_session_info.handle_key(key);
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
should_render
|
should_render
|
||||||
}
|
}
|
||||||
fn handle_attach_to_session(&mut self, key: Key) -> bool {
|
fn handle_attach_to_session(&mut self, key: KeyWithModifier) -> bool {
|
||||||
let mut should_render = false;
|
let mut should_render = false;
|
||||||
if self.show_kill_all_sessions_warning {
|
if self.show_kill_all_sessions_warning {
|
||||||
if let Key::Char('y') = key {
|
match key.bare_key {
|
||||||
let all_other_sessions = self.sessions.all_other_sessions();
|
BareKey::Char('y') if key.has_no_modifiers() => {
|
||||||
kill_sessions(&all_other_sessions);
|
let all_other_sessions = self.sessions.all_other_sessions();
|
||||||
self.reset_selected_index();
|
kill_sessions(&all_other_sessions);
|
||||||
self.search_term.clear();
|
self.reset_selected_index();
|
||||||
self.sessions
|
self.search_term.clear();
|
||||||
.update_search_term(&self.search_term, &self.colors);
|
self.sessions
|
||||||
self.show_kill_all_sessions_warning = false
|
.update_search_term(&self.search_term, &self.colors);
|
||||||
} else if let Key::Char('n') | Key::Esc | Key::Ctrl('c') = key {
|
self.show_kill_all_sessions_warning = false;
|
||||||
self.show_kill_all_sessions_warning = false
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Char('n') | BareKey::Esc if key.has_no_modifiers() => {
|
||||||
|
self.show_kill_all_sessions_warning = false;
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
|
self.show_kill_all_sessions_warning = false;
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
should_render = true;
|
} else {
|
||||||
} else if let Key::Right = key {
|
match key.bare_key {
|
||||||
self.sessions.result_expand();
|
BareKey::Right if key.has_no_modifiers() => {
|
||||||
should_render = true;
|
self.sessions.result_expand();
|
||||||
} else if let Key::Left = key {
|
should_render = true;
|
||||||
self.sessions.result_shrink();
|
},
|
||||||
should_render = true;
|
BareKey::Left if key.has_no_modifiers() => {
|
||||||
} else if let Key::Down = key {
|
self.sessions.result_shrink();
|
||||||
self.sessions.move_selection_down();
|
should_render = true;
|
||||||
should_render = true;
|
},
|
||||||
} else if let Key::Up = key {
|
BareKey::Down if key.has_no_modifiers() => {
|
||||||
self.sessions.move_selection_up();
|
self.sessions.move_selection_down();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
} else if let Key::Char(character) = key {
|
},
|
||||||
if character == '\n' {
|
BareKey::Up if key.has_no_modifiers() => {
|
||||||
self.handle_selection();
|
self.sessions.move_selection_up();
|
||||||
} else if let Some(new_session_name) = self.renaming_session_name.as_mut() {
|
should_render = true;
|
||||||
new_session_name.push(character);
|
},
|
||||||
} else {
|
BareKey::Char(character) if key.has_no_modifiers() => {
|
||||||
self.search_term.push(character);
|
if character == '\n' {
|
||||||
self.sessions
|
self.handle_selection();
|
||||||
.update_search_term(&self.search_term, &self.colors);
|
} else if let Some(new_session_name) = self.renaming_session_name.as_mut() {
|
||||||
}
|
new_session_name.push(character);
|
||||||
should_render = true;
|
} else {
|
||||||
} else if let Key::Backspace = key {
|
self.search_term.push(character);
|
||||||
if let Some(new_session_name) = self.renaming_session_name.as_mut() {
|
self.sessions
|
||||||
if new_session_name.is_empty() {
|
.update_search_term(&self.search_term, &self.colors);
|
||||||
self.renaming_session_name = None;
|
}
|
||||||
} else {
|
should_render = true;
|
||||||
new_session_name.pop();
|
},
|
||||||
}
|
BareKey::Backspace if key.has_no_modifiers() => {
|
||||||
} else {
|
if let Some(new_session_name) = self.renaming_session_name.as_mut() {
|
||||||
self.search_term.pop();
|
if new_session_name.is_empty() {
|
||||||
self.sessions
|
self.renaming_session_name = None;
|
||||||
.update_search_term(&self.search_term, &self.colors);
|
} else {
|
||||||
}
|
new_session_name.pop();
|
||||||
should_render = true;
|
}
|
||||||
} else if let Key::Ctrl('w') = key {
|
} else {
|
||||||
self.active_screen = ActiveScreen::NewSession;
|
self.search_term.pop();
|
||||||
should_render = true;
|
self.sessions
|
||||||
} else if let Key::Ctrl('r') = key {
|
.update_search_term(&self.search_term, &self.colors);
|
||||||
self.renaming_session_name = Some(String::new());
|
}
|
||||||
should_render = true;
|
should_render = true;
|
||||||
} else if let Key::Delete = key {
|
},
|
||||||
if let Some(selected_session_name) = self.sessions.get_selected_session_name() {
|
BareKey::Char('w') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
kill_sessions(&[selected_session_name]);
|
self.active_screen = ActiveScreen::NewSession;
|
||||||
self.reset_selected_index();
|
should_render = true;
|
||||||
self.search_term.clear();
|
},
|
||||||
self.sessions
|
BareKey::Char('r') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
.update_search_term(&self.search_term, &self.colors);
|
self.renaming_session_name = Some(String::new());
|
||||||
} else {
|
should_render = true;
|
||||||
self.show_error("Must select session before killing it.");
|
},
|
||||||
}
|
BareKey::Delete if key.has_no_modifiers() => {
|
||||||
should_render = true;
|
if let Some(selected_session_name) = self.sessions.get_selected_session_name() {
|
||||||
} else if let Key::Ctrl('d') = key {
|
kill_sessions(&[selected_session_name]);
|
||||||
let all_other_sessions = self.sessions.all_other_sessions();
|
self.reset_selected_index();
|
||||||
if all_other_sessions.is_empty() {
|
self.search_term.clear();
|
||||||
self.show_error("No other sessions to kill. Quit to kill the current one.");
|
self.sessions
|
||||||
} else {
|
.update_search_term(&self.search_term, &self.colors);
|
||||||
self.show_kill_all_sessions_warning = true;
|
} else {
|
||||||
}
|
self.show_error("Must select session before killing it.");
|
||||||
should_render = true;
|
}
|
||||||
} else if let Key::Ctrl('x') = key {
|
should_render = true;
|
||||||
disconnect_other_clients();
|
},
|
||||||
} else if let Key::Ctrl('c') = key {
|
BareKey::Char('d') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
if !self.search_term.is_empty() {
|
let all_other_sessions = self.sessions.all_other_sessions();
|
||||||
self.search_term.clear();
|
if all_other_sessions.is_empty() {
|
||||||
self.sessions
|
self.show_error("No other sessions to kill. Quit to kill the current one.");
|
||||||
.update_search_term(&self.search_term, &self.colors);
|
} else {
|
||||||
self.reset_selected_index();
|
self.show_kill_all_sessions_warning = true;
|
||||||
} else if !self.is_welcome_screen {
|
}
|
||||||
self.reset_selected_index();
|
should_render = true;
|
||||||
hide_self();
|
},
|
||||||
}
|
BareKey::Char('x') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
should_render = true;
|
disconnect_other_clients()
|
||||||
} else if let Key::BackTab = key {
|
},
|
||||||
self.toggle_active_screen();
|
BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
should_render = true;
|
if !self.search_term.is_empty() {
|
||||||
} else if let Key::Esc = key {
|
self.search_term.clear();
|
||||||
if self.renaming_session_name.is_some() {
|
self.sessions
|
||||||
self.renaming_session_name = None;
|
.update_search_term(&self.search_term, &self.colors);
|
||||||
should_render = true;
|
self.reset_selected_index();
|
||||||
} else if !self.is_welcome_screen {
|
} else if !self.is_welcome_screen {
|
||||||
hide_self();
|
self.reset_selected_index();
|
||||||
|
hide_self();
|
||||||
|
}
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Tab if key.has_no_modifiers() => {
|
||||||
|
self.toggle_active_screen();
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Esc if key.has_no_modifiers() => {
|
||||||
|
if self.renaming_session_name.is_some() {
|
||||||
|
self.renaming_session_name = None;
|
||||||
|
should_render = true;
|
||||||
|
} else if !self.is_welcome_screen {
|
||||||
|
hide_self();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
should_render
|
should_render
|
||||||
}
|
}
|
||||||
fn handle_resurrect_session_key(&mut self, key: Key) -> bool {
|
fn handle_resurrect_session_key(&mut self, key: KeyWithModifier) -> bool {
|
||||||
let mut should_render = false;
|
let mut should_render = false;
|
||||||
if let Key::Down = key {
|
match key.bare_key {
|
||||||
self.resurrectable_sessions.move_selection_down();
|
BareKey::Down if key.has_no_modifiers() => {
|
||||||
should_render = true;
|
self.resurrectable_sessions.move_selection_down();
|
||||||
} else if let Key::Up = key {
|
should_render = true;
|
||||||
self.resurrectable_sessions.move_selection_up();
|
},
|
||||||
should_render = true;
|
BareKey::Up if key.has_no_modifiers() => {
|
||||||
} else if let Key::Char(character) = key {
|
self.resurrectable_sessions.move_selection_up();
|
||||||
if character == '\n' {
|
should_render = true;
|
||||||
self.handle_selection();
|
},
|
||||||
} else {
|
BareKey::Char(character) if key.has_no_modifiers() => {
|
||||||
self.resurrectable_sessions.handle_character(character);
|
if character == '\n' {
|
||||||
}
|
self.handle_selection();
|
||||||
should_render = true;
|
} else {
|
||||||
} else if let Key::Backspace = key {
|
self.resurrectable_sessions.handle_character(character);
|
||||||
self.resurrectable_sessions.handle_backspace();
|
}
|
||||||
should_render = true;
|
should_render = true;
|
||||||
} else if let Key::Ctrl('w') = key {
|
},
|
||||||
self.active_screen = ActiveScreen::NewSession;
|
BareKey::Backspace if key.has_no_modifiers() => {
|
||||||
should_render = true;
|
self.resurrectable_sessions.handle_backspace();
|
||||||
} else if let Key::BackTab = key {
|
should_render = true;
|
||||||
self.toggle_active_screen();
|
},
|
||||||
should_render = true;
|
BareKey::Char('w') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
} else if let Key::Delete = key {
|
self.active_screen = ActiveScreen::NewSession;
|
||||||
self.resurrectable_sessions.delete_selected_session();
|
should_render = true;
|
||||||
should_render = true;
|
},
|
||||||
} else if let Key::Ctrl('d') = key {
|
BareKey::Tab if key.has_no_modifiers() => {
|
||||||
self.resurrectable_sessions
|
self.toggle_active_screen();
|
||||||
.show_delete_all_sessions_warning();
|
should_render = true;
|
||||||
should_render = true;
|
},
|
||||||
} else if let Key::Esc = key {
|
BareKey::Delete if key.has_no_modifiers() => {
|
||||||
if !self.is_welcome_screen {
|
self.resurrectable_sessions.delete_selected_session();
|
||||||
hide_self();
|
should_render = true;
|
||||||
}
|
},
|
||||||
|
BareKey::Char('d') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
|
self.resurrectable_sessions
|
||||||
|
.show_delete_all_sessions_warning();
|
||||||
|
should_render = true;
|
||||||
|
},
|
||||||
|
BareKey::Esc if key.has_no_modifiers() => {
|
||||||
|
if !self.is_welcome_screen {
|
||||||
|
hide_self();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
should_render
|
should_render
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,21 +70,24 @@ impl NewSessionInfo {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn handle_key(&mut self, key: Key) {
|
pub fn handle_key(&mut self, key: KeyWithModifier) {
|
||||||
match key {
|
match key.bare_key {
|
||||||
Key::Backspace => {
|
BareKey::Backspace if key.has_no_modifiers() => {
|
||||||
self.handle_backspace();
|
self.handle_backspace();
|
||||||
},
|
},
|
||||||
Key::Ctrl('c') | Key::Esc => {
|
BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
self.handle_break();
|
self.handle_break();
|
||||||
},
|
},
|
||||||
Key::Char(character) => {
|
BareKey::Esc if key.has_no_modifiers() => {
|
||||||
|
self.handle_break();
|
||||||
|
},
|
||||||
|
BareKey::Char(character) if key.has_no_modifiers() => {
|
||||||
self.add_char(character);
|
self.add_char(character);
|
||||||
},
|
},
|
||||||
Key::Up => {
|
BareKey::Up if key.has_no_modifiers() => {
|
||||||
self.move_selection_up();
|
self.move_selection_up();
|
||||||
},
|
},
|
||||||
Key::Down => {
|
BareKey::Down if key.has_no_modifiers() => {
|
||||||
self.move_selection_down();
|
self.move_selection_down();
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ use zellij_tile::prelude::*;
|
||||||
|
|
||||||
use crate::color_elements;
|
use crate::color_elements;
|
||||||
use crate::{
|
use crate::{
|
||||||
action_key, action_key_group, get_common_modifier, style_key_with_modifier, TO_NORMAL,
|
action_key, action_key_group, get_common_modifiers, style_key_with_modifier, TO_NORMAL,
|
||||||
};
|
};
|
||||||
use crate::{ColoredElements, LinePart};
|
use crate::{ColoredElements, LinePart};
|
||||||
|
|
||||||
struct KeyShortcut {
|
struct KeyShortcut {
|
||||||
mode: KeyMode,
|
mode: KeyMode,
|
||||||
action: KeyAction,
|
action: KeyAction,
|
||||||
key: Option<Key>,
|
key: Option<KeyWithModifier>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
|
@ -35,7 +35,7 @@ enum KeyMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyShortcut {
|
impl KeyShortcut {
|
||||||
pub fn new(mode: KeyMode, action: KeyAction, key: Option<Key>) -> Self {
|
pub fn new(mode: KeyMode, action: KeyAction, key: Option<KeyWithModifier>) -> Self {
|
||||||
KeyShortcut { mode, action, key }
|
KeyShortcut { mode, action, key }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,25 +52,36 @@ impl KeyShortcut {
|
||||||
KeyAction::Tmux => String::from("TMUX"),
|
KeyAction::Tmux => String::from("TMUX"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn letter_shortcut(&self, with_prefix: bool) -> String {
|
pub fn with_shortened_modifiers(&self, common_modifiers: &Vec<KeyModifier>) -> String {
|
||||||
let key = match self.key {
|
let key = match &self.key {
|
||||||
Some(k) => k,
|
Some(k) => k.strip_common_modifiers(common_modifiers),
|
||||||
None => return String::from("?"),
|
None => return String::from("?"),
|
||||||
};
|
};
|
||||||
if with_prefix {
|
let shortened_modifiers = key
|
||||||
|
.key_modifiers
|
||||||
|
.iter()
|
||||||
|
.map(|m| match m {
|
||||||
|
KeyModifier::Ctrl => "^C",
|
||||||
|
KeyModifier::Alt => "^A",
|
||||||
|
KeyModifier::Super => "^Su",
|
||||||
|
KeyModifier::Shift => "^Sh",
|
||||||
|
_ => "",
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("-");
|
||||||
|
if shortened_modifiers.is_empty() {
|
||||||
format!("{}", key)
|
format!("{}", key)
|
||||||
} else {
|
} else {
|
||||||
match key {
|
format!("{} {}", shortened_modifiers, key.bare_key)
|
||||||
Key::F(c) => format!("{}", c),
|
|
||||||
Key::CtrlF(n) => format!("F{}", n),
|
|
||||||
Key::AltF(n) => format!("F{}", n),
|
|
||||||
Key::Ctrl(c) => format!("{}", c),
|
|
||||||
Key::Char(_) => format!("{}", key),
|
|
||||||
Key::Alt(c) => format!("{}", c),
|
|
||||||
_ => String::from("??"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn letter_shortcut(&self, common_modifiers: &Vec<KeyModifier>) -> String {
|
||||||
|
let key = match &self.key {
|
||||||
|
Some(k) => k.strip_common_modifiers(common_modifiers),
|
||||||
|
None => return String::from("?"),
|
||||||
|
};
|
||||||
|
format!("{}", key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate long mode shortcut tile.
|
/// Generate long mode shortcut tile.
|
||||||
|
|
@ -96,14 +107,15 @@ fn long_mode_shortcut(
|
||||||
key: &KeyShortcut,
|
key: &KeyShortcut,
|
||||||
palette: ColoredElements,
|
palette: ColoredElements,
|
||||||
separator: &str,
|
separator: &str,
|
||||||
shared_super: bool,
|
common_modifiers: &Vec<KeyModifier>,
|
||||||
first_tile: bool,
|
first_tile: bool,
|
||||||
) -> LinePart {
|
) -> LinePart {
|
||||||
let key_hint = key.full_text();
|
let key_hint = key.full_text();
|
||||||
|
let has_common_modifiers = !common_modifiers.is_empty();
|
||||||
let key_binding = match (&key.mode, &key.key) {
|
let key_binding = match (&key.mode, &key.key) {
|
||||||
(KeyMode::Disabled, None) => "".to_string(),
|
(KeyMode::Disabled, None) => "".to_string(),
|
||||||
(_, None) => return LinePart::default(),
|
(_, None) => return LinePart::default(),
|
||||||
(_, Some(_)) => key.letter_shortcut(!shared_super),
|
(_, Some(_)) => key.letter_shortcut(common_modifiers),
|
||||||
};
|
};
|
||||||
|
|
||||||
let colors = match key.mode {
|
let colors = match key.mode {
|
||||||
|
|
@ -112,7 +124,59 @@ fn long_mode_shortcut(
|
||||||
KeyMode::Selected => palette.selected,
|
KeyMode::Selected => palette.selected,
|
||||||
KeyMode::Disabled => palette.disabled,
|
KeyMode::Disabled => palette.disabled,
|
||||||
};
|
};
|
||||||
let start_separator = if !shared_super && first_tile {
|
let start_separator = if !has_common_modifiers && first_tile {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
separator
|
||||||
|
};
|
||||||
|
let prefix_separator = colors.prefix_separator.paint(start_separator);
|
||||||
|
let char_left_separator = colors.char_left_separator.paint(" <".to_string());
|
||||||
|
let char_shortcut = colors.char_shortcut.paint(key_binding.to_string());
|
||||||
|
let char_right_separator = colors.char_right_separator.paint("> ".to_string());
|
||||||
|
let styled_text = colors.styled_text.paint(format!("{} ", key_hint));
|
||||||
|
let suffix_separator = colors.suffix_separator.paint(separator);
|
||||||
|
LinePart {
|
||||||
|
part: ANSIStrings(&[
|
||||||
|
prefix_separator,
|
||||||
|
char_left_separator,
|
||||||
|
char_shortcut,
|
||||||
|
char_right_separator,
|
||||||
|
styled_text,
|
||||||
|
suffix_separator,
|
||||||
|
])
|
||||||
|
.to_string(),
|
||||||
|
len: start_separator.chars().count() // Separator
|
||||||
|
+ 2 // " <"
|
||||||
|
+ key_binding.chars().count() // Key binding
|
||||||
|
+ 2 // "> "
|
||||||
|
+ key_hint.chars().count() // Key hint (mode)
|
||||||
|
+ 1 // " "
|
||||||
|
+ separator.chars().count(), // Separator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shortened_modifier_shortcut(
|
||||||
|
key: &KeyShortcut,
|
||||||
|
palette: ColoredElements,
|
||||||
|
separator: &str,
|
||||||
|
common_modifiers: &Vec<KeyModifier>,
|
||||||
|
first_tile: bool,
|
||||||
|
) -> LinePart {
|
||||||
|
let key_hint = key.full_text();
|
||||||
|
let has_common_modifiers = !common_modifiers.is_empty();
|
||||||
|
let key_binding = match (&key.mode, &key.key) {
|
||||||
|
(KeyMode::Disabled, None) => "".to_string(),
|
||||||
|
(_, None) => return LinePart::default(),
|
||||||
|
(_, Some(_)) => key.with_shortened_modifiers(common_modifiers),
|
||||||
|
};
|
||||||
|
|
||||||
|
let colors = match key.mode {
|
||||||
|
KeyMode::Unselected => palette.unselected,
|
||||||
|
KeyMode::UnselectedAlternate => palette.unselected_alternate,
|
||||||
|
KeyMode::Selected => palette.selected,
|
||||||
|
KeyMode::Disabled => palette.disabled,
|
||||||
|
};
|
||||||
|
let start_separator = if !has_common_modifiers && first_tile {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
separator
|
separator
|
||||||
|
|
@ -165,13 +229,14 @@ fn short_mode_shortcut(
|
||||||
key: &KeyShortcut,
|
key: &KeyShortcut,
|
||||||
palette: ColoredElements,
|
palette: ColoredElements,
|
||||||
separator: &str,
|
separator: &str,
|
||||||
shared_super: bool,
|
common_modifiers: &Vec<KeyModifier>,
|
||||||
first_tile: bool,
|
first_tile: bool,
|
||||||
) -> LinePart {
|
) -> LinePart {
|
||||||
|
let has_common_modifiers = !common_modifiers.is_empty();
|
||||||
let key_binding = match (&key.mode, &key.key) {
|
let key_binding = match (&key.mode, &key.key) {
|
||||||
(KeyMode::Disabled, None) => "".to_string(),
|
(KeyMode::Disabled, None) => "".to_string(),
|
||||||
(_, None) => return LinePart::default(),
|
(_, None) => return LinePart::default(),
|
||||||
(_, Some(_)) => key.letter_shortcut(!shared_super),
|
(_, Some(_)) => key.letter_shortcut(common_modifiers),
|
||||||
};
|
};
|
||||||
|
|
||||||
let colors = match key.mode {
|
let colors = match key.mode {
|
||||||
|
|
@ -180,7 +245,7 @@ fn short_mode_shortcut(
|
||||||
KeyMode::Selected => palette.selected,
|
KeyMode::Selected => palette.selected,
|
||||||
KeyMode::Disabled => palette.disabled,
|
KeyMode::Disabled => palette.disabled,
|
||||||
};
|
};
|
||||||
let start_separator = if !shared_super && first_tile {
|
let start_separator = if !has_common_modifiers && first_tile {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
separator
|
separator
|
||||||
|
|
@ -206,11 +271,23 @@ fn key_indicators(
|
||||||
mode_info: &ModeInfo,
|
mode_info: &ModeInfo,
|
||||||
) -> LinePart {
|
) -> LinePart {
|
||||||
// Print full-width hints
|
// Print full-width hints
|
||||||
let mut line_part = superkey(palette, separator, mode_info);
|
let (shared_modifiers, mut line_part) = superkey(palette, separator, mode_info);
|
||||||
let shared_super = line_part.len > 0;
|
for key in keys {
|
||||||
for ctrl_key in keys {
|
|
||||||
let line_empty = line_part.len == 0;
|
let line_empty = line_part.len == 0;
|
||||||
let key = long_mode_shortcut(ctrl_key, palette, separator, shared_super, line_empty);
|
let key = long_mode_shortcut(key, palette, separator, &shared_modifiers, line_empty);
|
||||||
|
line_part.part = format!("{}{}", line_part.part, key.part);
|
||||||
|
line_part.len += key.len;
|
||||||
|
}
|
||||||
|
if line_part.len < max_len {
|
||||||
|
return line_part;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full-width doesn't fit, try shortened modifiers (eg. "^C" instead of "Ctrl")
|
||||||
|
line_part = superkey(palette, separator, mode_info).1;
|
||||||
|
for key in keys {
|
||||||
|
let line_empty = line_part.len == 0;
|
||||||
|
let key =
|
||||||
|
shortened_modifier_shortcut(key, palette, separator, &shared_modifiers, line_empty);
|
||||||
line_part.part = format!("{}{}", line_part.part, key.part);
|
line_part.part = format!("{}{}", line_part.part, key.part);
|
||||||
line_part.len += key.len;
|
line_part.len += key.len;
|
||||||
}
|
}
|
||||||
|
|
@ -219,11 +296,10 @@ fn key_indicators(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full-width doesn't fit, try shortened hints (just keybindings, no meanings/actions)
|
// Full-width doesn't fit, try shortened hints (just keybindings, no meanings/actions)
|
||||||
line_part = superkey(palette, separator, mode_info);
|
line_part = superkey(palette, separator, mode_info).1;
|
||||||
let shared_super = line_part.len > 0;
|
for key in keys {
|
||||||
for ctrl_key in keys {
|
|
||||||
let line_empty = line_part.len == 0;
|
let line_empty = line_part.len == 0;
|
||||||
let key = short_mode_shortcut(ctrl_key, palette, separator, shared_super, line_empty);
|
let key = short_mode_shortcut(key, palette, separator, &shared_modifiers, line_empty);
|
||||||
line_part.part = format!("{}{}", line_part.part, key.part);
|
line_part.part = format!("{}{}", line_part.part, key.part);
|
||||||
line_part.len += key.len;
|
line_part.len += key.len;
|
||||||
}
|
}
|
||||||
|
|
@ -345,7 +421,7 @@ fn swap_layout_status(
|
||||||
/// to get back to normal mode from any input mode, but they aren't of interest when searching
|
/// to get back to normal mode from any input mode, but they aren't of interest when searching
|
||||||
/// for the super key. If for any input mode the user has bound only these keys to switching back
|
/// for the super key. If for any input mode the user has bound only these keys to switching back
|
||||||
/// to `InputMode::Normal`, a '?' will be displayed as keybinding instead.
|
/// to `InputMode::Normal`, a '?' will be displayed as keybinding instead.
|
||||||
pub fn mode_switch_keys(mode_info: &ModeInfo) -> Vec<Key> {
|
pub fn mode_switch_keys(mode_info: &ModeInfo) -> Vec<KeyWithModifier> {
|
||||||
mode_info
|
mode_info
|
||||||
.get_mode_keybinds()
|
.get_mode_keybinds()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -355,7 +431,19 @@ pub fn mode_switch_keys(mode_info: &ModeInfo) -> Vec<Key> {
|
||||||
Some(vac) => {
|
Some(vac) => {
|
||||||
// We ignore certain "default" keybindings that switch back to normal InputMode.
|
// We ignore certain "default" keybindings that switch back to normal InputMode.
|
||||||
// These include: ' ', '\n', 'Esc'
|
// These include: ' ', '\n', 'Esc'
|
||||||
if matches!(key, Key::Char(' ') | Key::Char('\n') | Key::Esc) {
|
if matches!(
|
||||||
|
key,
|
||||||
|
KeyWithModifier {
|
||||||
|
bare_key: BareKey::Char(' '),
|
||||||
|
..
|
||||||
|
} | KeyWithModifier {
|
||||||
|
bare_key: BareKey::Enter,
|
||||||
|
..
|
||||||
|
} | KeyWithModifier {
|
||||||
|
bare_key: BareKey::Esc,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let actions::Action::SwitchToMode(mode) = vac {
|
if let actions::Action::SwitchToMode(mode) = vac {
|
||||||
|
|
@ -368,12 +456,12 @@ pub fn mode_switch_keys(mode_info: &ModeInfo) -> Vec<Key> {
|
||||||
| InputMode::Resize
|
| InputMode::Resize
|
||||||
| InputMode::Move
|
| InputMode::Move
|
||||||
| InputMode::Scroll
|
| InputMode::Scroll
|
||||||
| InputMode::Session => Some(*key),
|
| InputMode::Session => Some(key.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if let actions::Action::Quit = vac {
|
if let actions::Action::Quit = vac {
|
||||||
return Some(*key);
|
return Some(key.clone());
|
||||||
}
|
}
|
||||||
// Not a `SwitchToMode` or `Quit` action, ignore
|
// Not a `SwitchToMode` or `Quit` action, ignore
|
||||||
None
|
None
|
||||||
|
|
@ -382,36 +470,69 @@ pub fn mode_switch_keys(mode_info: &ModeInfo) -> Vec<Key> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn superkey(palette: ColoredElements, separator: &str, mode_info: &ModeInfo) -> LinePart {
|
pub fn superkey(
|
||||||
|
palette: ColoredElements,
|
||||||
|
separator: &str,
|
||||||
|
mode_info: &ModeInfo,
|
||||||
|
) -> (Vec<KeyModifier>, LinePart) {
|
||||||
// Find a common modifier if any
|
// Find a common modifier if any
|
||||||
let prefix_text = match get_common_modifier(mode_switch_keys(mode_info).iter().collect()) {
|
let common_modifiers = get_common_modifiers(mode_switch_keys(mode_info).iter().collect());
|
||||||
Some(text) => {
|
if common_modifiers.is_empty() {
|
||||||
if mode_info.capabilities.arrow_fonts {
|
return (common_modifiers, LinePart::default());
|
||||||
// Add extra space in simplified ui
|
}
|
||||||
format!(" {} + ", text)
|
|
||||||
} else {
|
let prefix_text = if mode_info.capabilities.arrow_fonts {
|
||||||
format!(" {} +", text)
|
// Add extra space in simplified ui
|
||||||
}
|
format!(
|
||||||
},
|
" {} + ",
|
||||||
_ => return LinePart::default(),
|
common_modifiers
|
||||||
|
.iter()
|
||||||
|
.map(|m| m.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("-")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
" {} +",
|
||||||
|
common_modifiers
|
||||||
|
.iter()
|
||||||
|
.map(|m| m.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("-")
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix = palette.superkey_prefix.paint(&prefix_text);
|
let prefix = palette.superkey_prefix.paint(&prefix_text);
|
||||||
let suffix_separator = palette.superkey_suffix_separator.paint(separator);
|
let suffix_separator = palette.superkey_suffix_separator.paint(separator);
|
||||||
LinePart {
|
(
|
||||||
part: ANSIStrings(&[prefix, suffix_separator]).to_string(),
|
common_modifiers,
|
||||||
len: prefix_text.chars().count() + separator.chars().count(),
|
LinePart {
|
||||||
}
|
part: ANSIStrings(&[prefix, suffix_separator]).to_string(),
|
||||||
|
len: prefix_text.chars().count() + separator.chars().count(),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_char(kv: Vec<Key>) -> Option<Key> {
|
pub fn to_char(kv: Vec<KeyWithModifier>) -> Option<KeyWithModifier> {
|
||||||
let key = kv
|
let key = kv
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|key| {
|
.filter(|key| {
|
||||||
// These are general "keybindings" to get back to normal, they aren't interesting here.
|
// These are general "keybindings" to get back to normal, they aren't interesting here.
|
||||||
!matches!(key, Key::Char('\n') | Key::Char(' ') | Key::Esc)
|
!matches!(
|
||||||
|
key,
|
||||||
|
KeyWithModifier {
|
||||||
|
bare_key: BareKey::Enter,
|
||||||
|
..
|
||||||
|
} | KeyWithModifier {
|
||||||
|
bare_key: BareKey::Char(' '),
|
||||||
|
..
|
||||||
|
} | KeyWithModifier {
|
||||||
|
bare_key: BareKey::Esc,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<&Key>>()
|
.collect::<Vec<&KeyWithModifier>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next();
|
.next();
|
||||||
// Maybe the user bound one of the ignored keys?
|
// Maybe the user bound one of the ignored keys?
|
||||||
|
|
@ -593,10 +714,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn long_mode_shortcut_selected_with_binding() {
|
fn long_mode_shortcut_selected_with_binding() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Char('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
||||||
|
|
@ -608,11 +733,11 @@ mod tests {
|
||||||
let key = KeyShortcut::new(
|
let key = KeyShortcut::new(
|
||||||
KeyMode::Unselected,
|
KeyMode::Unselected,
|
||||||
KeyAction::Session,
|
KeyAction::Session,
|
||||||
Some(Key::Char('0')),
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
);
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
||||||
|
|
@ -624,11 +749,11 @@ mod tests {
|
||||||
let key = KeyShortcut::new(
|
let key = KeyShortcut::new(
|
||||||
KeyMode::UnselectedAlternate,
|
KeyMode::UnselectedAlternate,
|
||||||
KeyAction::Session,
|
KeyAction::Session,
|
||||||
Some(Key::Char('0')),
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
);
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
||||||
|
|
@ -640,7 +765,7 @@ mod tests {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, None);
|
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, None);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "".to_string());
|
assert_eq!(ret, "".to_string());
|
||||||
|
|
@ -649,10 +774,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
// First tile doesn't print a starting separator
|
// First tile doesn't print a starting separator
|
||||||
fn long_mode_shortcut_selected_with_binding_first_tile() {
|
fn long_mode_shortcut_selected_with_binding_first_tile() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Char('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, true);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], true);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, " <0> SESSION +".to_string());
|
assert_eq!(ret, " <0> SESSION +".to_string());
|
||||||
|
|
@ -661,10 +790,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
// Modifier is the superkey, mustn't appear in angled brackets
|
// Modifier is the superkey, mustn't appear in angled brackets
|
||||||
fn long_mode_shortcut_selected_with_ctrl_binding_shared_superkey() {
|
fn long_mode_shortcut_selected_with_ctrl_binding_shared_superkey() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Ctrl('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", true, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![KeyModifier::Ctrl], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
||||||
|
|
@ -673,22 +806,30 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
// Modifier must be in the angled brackets
|
// Modifier must be in the angled brackets
|
||||||
fn long_mode_shortcut_selected_with_ctrl_binding_no_shared_superkey() {
|
fn long_mode_shortcut_selected_with_ctrl_binding_no_shared_superkey() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Ctrl('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <Ctrl+0> SESSION +".to_string());
|
assert_eq!(ret, "+ <Ctrl 0> SESSION +".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Must be displayed as usual, but it is styled to be greyed out which we don't test here
|
// Must be displayed as usual, but it is styled to be greyed out which we don't test here
|
||||||
fn long_mode_shortcut_disabled_with_binding() {
|
fn long_mode_shortcut_disabled_with_binding() {
|
||||||
let key = KeyShortcut::new(KeyMode::Disabled, KeyAction::Session, Some(Key::Char('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Disabled,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
||||||
|
|
@ -700,7 +841,7 @@ mod tests {
|
||||||
let key = KeyShortcut::new(KeyMode::Disabled, KeyAction::Session, None);
|
let key = KeyShortcut::new(KeyMode::Disabled, KeyAction::Session, None);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", false, false);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <> SESSION +".to_string());
|
assert_eq!(ret, "+ <> SESSION +".to_string());
|
||||||
|
|
@ -711,10 +852,14 @@ mod tests {
|
||||||
// Note that when "shared_super" is true, the tile **cannot** be the first on the line, so we
|
// Note that when "shared_super" is true, the tile **cannot** be the first on the line, so we
|
||||||
// ignore **first** here.
|
// ignore **first** here.
|
||||||
fn long_mode_shortcut_selected_with_ctrl_binding_and_shared_super_and_first_tile() {
|
fn long_mode_shortcut_selected_with_ctrl_binding_and_shared_super_and_first_tile() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Ctrl('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = long_mode_shortcut(&key, color, "+", true, true);
|
let ret = long_mode_shortcut(&key, color, "+", &vec![KeyModifier::Ctrl], true);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
assert_eq!(ret, "+ <0> SESSION +".to_string());
|
||||||
|
|
@ -722,10 +867,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn short_mode_shortcut_selected_with_binding() {
|
fn short_mode_shortcut_selected_with_binding() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Char('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ 0 +".to_string());
|
assert_eq!(ret, "+ 0 +".to_string());
|
||||||
|
|
@ -733,21 +882,29 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn short_mode_shortcut_selected_with_ctrl_binding_no_shared_super() {
|
fn short_mode_shortcut_selected_with_ctrl_binding_no_shared_super() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Ctrl('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ Ctrl+0 +".to_string());
|
assert_eq!(ret, "+ Ctrl 0 +".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn short_mode_shortcut_selected_with_ctrl_binding_shared_super() {
|
fn short_mode_shortcut_selected_with_ctrl_binding_shared_super() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Ctrl('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", true, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![KeyModifier::Ctrl], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ 0 +".to_string());
|
assert_eq!(ret, "+ 0 +".to_string());
|
||||||
|
|
@ -755,10 +912,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn short_mode_shortcut_selected_with_binding_first_tile() {
|
fn short_mode_shortcut_selected_with_binding_first_tile() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Char('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, true);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], true);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, " 0 +".to_string());
|
assert_eq!(ret, " 0 +".to_string());
|
||||||
|
|
@ -769,11 +930,11 @@ mod tests {
|
||||||
let key = KeyShortcut::new(
|
let key = KeyShortcut::new(
|
||||||
KeyMode::Unselected,
|
KeyMode::Unselected,
|
||||||
KeyAction::Session,
|
KeyAction::Session,
|
||||||
Some(Key::Char('0')),
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
);
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ 0 +".to_string());
|
assert_eq!(ret, "+ 0 +".to_string());
|
||||||
|
|
@ -784,11 +945,11 @@ mod tests {
|
||||||
let key = KeyShortcut::new(
|
let key = KeyShortcut::new(
|
||||||
KeyMode::UnselectedAlternate,
|
KeyMode::UnselectedAlternate,
|
||||||
KeyAction::Session,
|
KeyAction::Session,
|
||||||
Some(Key::Char('0')),
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
);
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ 0 +".to_string());
|
assert_eq!(ret, "+ 0 +".to_string());
|
||||||
|
|
@ -796,10 +957,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn short_mode_shortcut_disabled_with_binding() {
|
fn short_mode_shortcut_disabled_with_binding() {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, Some(Key::Char('0')));
|
let key = KeyShortcut::new(
|
||||||
|
KeyMode::Selected,
|
||||||
|
KeyAction::Session,
|
||||||
|
Some(KeyWithModifier::new(BareKey::Char('0'))),
|
||||||
|
);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "+ 0 +".to_string());
|
assert_eq!(ret, "+ 0 +".to_string());
|
||||||
|
|
@ -810,7 +975,7 @@ mod tests {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, None);
|
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, None);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "".to_string());
|
assert_eq!(ret, "".to_string());
|
||||||
|
|
@ -821,7 +986,7 @@ mod tests {
|
||||||
let key = KeyShortcut::new(KeyMode::Unselected, KeyAction::Session, None);
|
let key = KeyShortcut::new(KeyMode::Unselected, KeyAction::Session, None);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "".to_string());
|
assert_eq!(ret, "".to_string());
|
||||||
|
|
@ -832,7 +997,7 @@ mod tests {
|
||||||
let key = KeyShortcut::new(KeyMode::UnselectedAlternate, KeyAction::Session, None);
|
let key = KeyShortcut::new(KeyMode::UnselectedAlternate, KeyAction::Session, None);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "".to_string());
|
assert_eq!(ret, "".to_string());
|
||||||
|
|
@ -843,7 +1008,7 @@ mod tests {
|
||||||
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, None);
|
let key = KeyShortcut::new(KeyMode::Selected, KeyAction::Session, None);
|
||||||
let color = colored_elements();
|
let color = colored_elements();
|
||||||
|
|
||||||
let ret = short_mode_shortcut(&key, color, "+", false, false);
|
let ret = short_mode_shortcut(&key, color, "+", &vec![], false);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, "".to_string());
|
assert_eq!(ret, "".to_string());
|
||||||
|
|
@ -857,9 +1022,9 @@ mod tests {
|
||||||
mode: InputMode::Normal,
|
mode: InputMode::Normal,
|
||||||
keybinds : vec![
|
keybinds : vec![
|
||||||
(InputMode::Normal, vec![
|
(InputMode::Normal, vec![
|
||||||
(Key::Ctrl('a'), vec![Action::SwitchToMode(InputMode::Pane)]),
|
(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Pane)]),
|
||||||
(Key::Ctrl('b'), vec![Action::SwitchToMode(InputMode::Resize)]),
|
(KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
(Key::Ctrl('c'), vec![Action::SwitchToMode(InputMode::Move)]),
|
(KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Move)]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
|
|
@ -881,9 +1046,9 @@ mod tests {
|
||||||
mode: InputMode::Normal,
|
mode: InputMode::Normal,
|
||||||
keybinds : vec![
|
keybinds : vec![
|
||||||
(InputMode::Normal, vec![
|
(InputMode::Normal, vec![
|
||||||
(Key::Ctrl('a'), vec![Action::SwitchToMode(InputMode::Pane)]),
|
(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Pane)]),
|
||||||
(Key::Ctrl('b'), vec![Action::SwitchToMode(InputMode::Resize)]),
|
(KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
(Key::Char('c'), vec![Action::SwitchToMode(InputMode::Move)]),
|
(KeyWithModifier::new(BareKey::Char('c')), vec![Action::SwitchToMode(InputMode::Move)]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
|
|
@ -894,7 +1059,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ret,
|
ret,
|
||||||
" <Ctrl+a> PANE >> <Ctrl+b> RESIZE >> <c> MOVE >".to_string()
|
" <Ctrl a> PANE >> <Ctrl b> RESIZE >> <c> MOVE >".to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -905,11 +1070,11 @@ mod tests {
|
||||||
mode: InputMode::Normal,
|
mode: InputMode::Normal,
|
||||||
keybinds : vec![
|
keybinds : vec![
|
||||||
(InputMode::Normal, vec![
|
(InputMode::Normal, vec![
|
||||||
(Key::Ctrl('a'), vec![Action::SwitchToMode(InputMode::Locked)]),
|
(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Locked)]),
|
||||||
(Key::Backspace, vec![Action::SwitchToMode(InputMode::Pane)]),
|
(KeyWithModifier::new(BareKey::Backspace), vec![Action::SwitchToMode(InputMode::Pane)]),
|
||||||
(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]),
|
(KeyWithModifier::new(BareKey::Enter), vec![Action::SwitchToMode(InputMode::Tab)]),
|
||||||
(Key::Char('\t'), vec![Action::SwitchToMode(InputMode::Resize)]),
|
(KeyWithModifier::new(BareKey::Tab), vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
(Key::Left, vec![Action::SwitchToMode(InputMode::Move)]),
|
(KeyWithModifier::new(BareKey::Left), vec![Action::SwitchToMode(InputMode::Move)]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
|
|
@ -920,7 +1085,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ret,
|
ret,
|
||||||
" <Ctrl+a> LOCK >> <BACKSPACE> PANE >> <ENTER> TAB >> <TAB> RESIZE >> <←> MOVE >"
|
" <Ctrl a> LOCK >> <BACKSPACE> PANE >> <ENTER> TAB >> <TAB> RESIZE >> <←> MOVE >"
|
||||||
.to_string()
|
.to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -932,11 +1097,11 @@ mod tests {
|
||||||
mode: InputMode::Normal,
|
mode: InputMode::Normal,
|
||||||
keybinds : vec![
|
keybinds : vec![
|
||||||
(InputMode::Normal, vec![
|
(InputMode::Normal, vec![
|
||||||
(Key::Ctrl('a'), vec![Action::SwitchToMode(InputMode::Locked)]),
|
(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Locked)]),
|
||||||
(Key::Ctrl('b'), vec![Action::SwitchToMode(InputMode::Pane)]),
|
(KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Pane)]),
|
||||||
(Key::Ctrl('c'), vec![Action::SwitchToMode(InputMode::Tab)]),
|
(KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Tab)]),
|
||||||
(Key::Ctrl('d'), vec![Action::SwitchToMode(InputMode::Resize)]),
|
(KeyWithModifier::new(BareKey::Char('d')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
(Key::Ctrl('e'), vec![Action::SwitchToMode(InputMode::Move)]),
|
(KeyWithModifier::new(BareKey::Char('e')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Move)]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
|
|
@ -955,9 +1120,9 @@ mod tests {
|
||||||
mode: InputMode::Normal,
|
mode: InputMode::Normal,
|
||||||
keybinds : vec![
|
keybinds : vec![
|
||||||
(InputMode::Normal, vec![
|
(InputMode::Normal, vec![
|
||||||
(Key::Ctrl('a'), vec![Action::SwitchToMode(InputMode::Pane)]),
|
(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Pane)]),
|
||||||
(Key::Ctrl('b'), vec![Action::SwitchToMode(InputMode::Resize)]),
|
(KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
(Key::Ctrl('c'), vec![Action::SwitchToMode(InputMode::Move)]),
|
(KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier(), vec![Action::SwitchToMode(InputMode::Move)]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
|
|
|
||||||
|
|
@ -324,30 +324,18 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a common modifier key from a key vector.
|
pub fn get_common_modifiers(mut keyvec: Vec<&KeyWithModifier>) -> Vec<KeyModifier> {
|
||||||
///
|
if keyvec.is_empty() {
|
||||||
/// Iterates over all keys and returns any found common modifier key. Possible modifiers that will
|
return vec![];
|
||||||
/// be detected are "Ctrl" and "Alt".
|
|
||||||
pub fn get_common_modifier(keyvec: Vec<&Key>) -> Option<String> {
|
|
||||||
let mut modifier = "";
|
|
||||||
let mut new_modifier;
|
|
||||||
for key in keyvec.iter() {
|
|
||||||
match key {
|
|
||||||
Key::Ctrl(_) | Key::CtrlF(_) => new_modifier = "Ctrl",
|
|
||||||
Key::Alt(_) | Key::AltF(_) => new_modifier = "Alt",
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
if modifier.is_empty() {
|
|
||||||
modifier = new_modifier;
|
|
||||||
} else if modifier != new_modifier {
|
|
||||||
// Prefix changed!
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
match modifier.is_empty() {
|
let mut common_modifiers = keyvec.pop().unwrap().key_modifiers.clone();
|
||||||
true => None,
|
for key in keyvec {
|
||||||
false => Some(modifier.to_string()),
|
common_modifiers = common_modifiers
|
||||||
|
.intersection(&key.key_modifiers)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
common_modifiers.into_iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get key from action pattern(s).
|
/// Get key from action pattern(s).
|
||||||
|
|
@ -356,7 +344,10 @@ pub fn get_common_modifier(keyvec: Vec<&Key>) -> Option<String> {
|
||||||
/// all keybindings for the current mode and one or more `p` patterns which match a sequence of
|
/// all keybindings for the current mode and one or more `p` patterns which match a sequence of
|
||||||
/// actions to search for. If within the keymap a sequence of actions matching `p` is found, all
|
/// actions to search for. If within the keymap a sequence of actions matching `p` is found, all
|
||||||
/// keys that trigger the action pattern are returned as vector of `Vec<Key>`.
|
/// keys that trigger the action pattern are returned as vector of `Vec<Key>`.
|
||||||
pub fn action_key(keymap: &[(Key, Vec<Action>)], action: &[Action]) -> Vec<Key> {
|
pub fn action_key(
|
||||||
|
keymap: &[(KeyWithModifier, Vec<Action>)],
|
||||||
|
action: &[Action],
|
||||||
|
) -> Vec<KeyWithModifier> {
|
||||||
keymap
|
keymap
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(key, acvec)| {
|
.filter_map(|(key, acvec)| {
|
||||||
|
|
@ -367,18 +358,21 @@ pub fn action_key(keymap: &[(Key, Vec<Action>)], action: &[Action]) -> Vec<Key>
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
if matching == acvec.len() && matching == action.len() {
|
if matching == acvec.len() && matching == action.len() {
|
||||||
Some(*key)
|
Some(key.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<Key>>()
|
.collect::<Vec<KeyWithModifier>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get multiple keys for multiple actions.
|
/// Get multiple keys for multiple actions.
|
||||||
///
|
///
|
||||||
/// An extension of [`action_key`] that iterates over all action tuples and collects the results.
|
/// An extension of [`action_key`] that iterates over all action tuples and collects the results.
|
||||||
pub fn action_key_group(keymap: &[(Key, Vec<Action>)], actions: &[&[Action]]) -> Vec<Key> {
|
pub fn action_key_group(
|
||||||
|
keymap: &[(KeyWithModifier, Vec<Action>)],
|
||||||
|
actions: &[&[Action]],
|
||||||
|
) -> Vec<KeyWithModifier> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
for action in actions {
|
for action in actions {
|
||||||
ret.extend(action_key(keymap, action));
|
ret.extend(action_key(keymap, action));
|
||||||
|
|
@ -406,11 +400,10 @@ pub fn action_key_group(keymap: &[(Key, Vec<Action>)], actions: &[&[Action]]) ->
|
||||||
/// The returned Vector of [`ANSIString`] is suitable for transformation into an [`ANSIStrings`]
|
/// The returned Vector of [`ANSIString`] is suitable for transformation into an [`ANSIStrings`]
|
||||||
/// type.
|
/// type.
|
||||||
pub fn style_key_with_modifier(
|
pub fn style_key_with_modifier(
|
||||||
keyvec: &[Key],
|
keyvec: &[KeyWithModifier],
|
||||||
palette: &Palette,
|
palette: &Palette,
|
||||||
background: Option<PaletteColor>,
|
background: Option<PaletteColor>,
|
||||||
) -> Vec<ANSIString<'static>> {
|
) -> Vec<ANSIString<'static>> {
|
||||||
// Nothing to do, quit...
|
|
||||||
if keyvec.is_empty() {
|
if keyvec.is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
@ -423,12 +416,18 @@ pub fn style_key_with_modifier(
|
||||||
let orange_color = palette_match!(palette.orange);
|
let orange_color = palette_match!(palette.orange);
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
|
|
||||||
// Prints modifier key
|
let common_modifiers = get_common_modifiers(keyvec.iter().collect());
|
||||||
let modifier_str = match get_common_modifier(keyvec.iter().collect()) {
|
|
||||||
Some(modifier) => modifier,
|
// let modifier_str = match get_common_modifier(keyvec.iter().collect()) {
|
||||||
None => "".to_string(),
|
// Some(modifier) => modifier,
|
||||||
};
|
// None => "".to_string(),
|
||||||
let no_modifier = modifier_str.is_empty();
|
// };
|
||||||
|
let no_common_modifier = common_modifiers.is_empty();
|
||||||
|
let modifier_str = common_modifiers
|
||||||
|
.iter()
|
||||||
|
.map(|m| m.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("-");
|
||||||
let painted_modifier = if modifier_str.is_empty() {
|
let painted_modifier = if modifier_str.is_empty() {
|
||||||
Style::new().paint("")
|
Style::new().paint("")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -446,7 +445,7 @@ pub fn style_key_with_modifier(
|
||||||
ret.push(painted_modifier);
|
ret.push(painted_modifier);
|
||||||
|
|
||||||
// Prints key group start
|
// Prints key group start
|
||||||
let group_start_str = if no_modifier { "<" } else { " + <" };
|
let group_start_str = if no_common_modifier { "<" } else { " + <" };
|
||||||
if let Some(background) = background {
|
if let Some(background) = background {
|
||||||
let background = palette_match!(background);
|
let background = palette_match!(background);
|
||||||
ret.push(
|
ret.push(
|
||||||
|
|
@ -463,15 +462,20 @@ pub fn style_key_with_modifier(
|
||||||
let key = keyvec
|
let key = keyvec
|
||||||
.iter()
|
.iter()
|
||||||
.map(|key| {
|
.map(|key| {
|
||||||
if no_modifier {
|
if no_common_modifier {
|
||||||
format!("{}", key)
|
format!("{}", key)
|
||||||
} else {
|
} else {
|
||||||
match key {
|
let key_modifier_for_key = key
|
||||||
Key::Ctrl(c) => format!("{}", Key::Char(*c)),
|
.key_modifiers
|
||||||
Key::CtrlF(n) => format!("{}", Key::F(*n)),
|
.iter()
|
||||||
Key::Alt(c) => format!("{}", c),
|
.filter(|m| !common_modifiers.contains(m))
|
||||||
Key::AltF(n) => format!("{}", Key::F(*n)),
|
.map(|m| m.to_string())
|
||||||
_ => format!("{}", key),
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
if key_modifier_for_key.is_empty() {
|
||||||
|
format!("{}", key.bare_key)
|
||||||
|
} else {
|
||||||
|
format!("{} {}", key_modifier_for_key, key.bare_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -538,20 +542,24 @@ pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ansi_term::unstyle;
|
use ansi_term::unstyle;
|
||||||
use ansi_term::ANSIStrings;
|
use ansi_term::ANSIStrings;
|
||||||
use zellij_tile::prelude::CharOrArrow;
|
|
||||||
use zellij_tile::prelude::Direction;
|
|
||||||
|
|
||||||
fn big_keymap() -> Vec<(Key, Vec<Action>)> {
|
fn big_keymap() -> Vec<(KeyWithModifier, Vec<Action>)> {
|
||||||
vec![
|
vec![
|
||||||
(Key::Char('a'), vec![Action::Quit]),
|
(KeyWithModifier::new(BareKey::Char('a')), vec![Action::Quit]),
|
||||||
(Key::Ctrl('b'), vec![Action::ScrollUp]),
|
|
||||||
(Key::Ctrl('d'), vec![Action::ScrollDown]),
|
|
||||||
(
|
(
|
||||||
Key::Alt(CharOrArrow::Char('c')),
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
|
vec![Action::ScrollUp],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('d')).with_ctrl_modifier(),
|
||||||
|
vec![Action::ScrollDown],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('c')).with_alt_modifier(),
|
||||||
vec![Action::ScrollDown, Action::SwitchToMode(InputMode::Normal)],
|
vec![Action::ScrollDown, Action::SwitchToMode(InputMode::Normal)],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Key::Char('1'),
|
KeyWithModifier::new(BareKey::Char('1')),
|
||||||
vec![TO_NORMAL, Action::SwitchToMode(InputMode::Locked)],
|
vec![TO_NORMAL, Action::SwitchToMode(InputMode::Locked)],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
@ -559,94 +567,65 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn common_modifier_with_ctrl_keys() {
|
fn common_modifier_with_ctrl_keys() {
|
||||||
let keyvec = vec![Key::Ctrl('a'), Key::Ctrl('b'), Key::Ctrl('c')];
|
let keyvec = vec![
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(),
|
||||||
assert_eq!(ret, Some("Ctrl".to_string()));
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier(),
|
||||||
|
];
|
||||||
|
let ret = get_common_modifiers(keyvec.iter().collect());
|
||||||
|
assert_eq!(ret, vec![KeyModifier::Ctrl]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn common_modifier_with_alt_keys_chars() {
|
fn common_modifier_with_alt_keys_chars() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Alt(CharOrArrow::Char('1')),
|
KeyWithModifier::new(BareKey::Char('1')).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('t')),
|
KeyWithModifier::new(BareKey::Char('t')).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('z')),
|
KeyWithModifier::new(BareKey::Char('z')).with_alt_modifier(),
|
||||||
];
|
];
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
let ret = get_common_modifiers(keyvec.iter().collect());
|
||||||
assert_eq!(ret, Some("Alt".to_string()));
|
assert_eq!(ret, vec![KeyModifier::Alt]);
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn common_modifier_with_alt_keys_arrows() {
|
|
||||||
let keyvec = vec![
|
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Left)),
|
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Right)),
|
|
||||||
];
|
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
|
||||||
assert_eq!(ret, Some("Alt".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn common_modifier_with_alt_keys_arrows_and_chars() {
|
|
||||||
let keyvec = vec![
|
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Left)),
|
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Right)),
|
|
||||||
Key::Alt(CharOrArrow::Char('t')),
|
|
||||||
Key::Alt(CharOrArrow::Char('z')),
|
|
||||||
];
|
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
|
||||||
assert_eq!(ret, Some("Alt".to_string()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn common_modifier_with_mixed_alt_ctrl_keys() {
|
fn common_modifier_with_mixed_alt_ctrl_keys() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Left)),
|
KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('z')),
|
KeyWithModifier::new(BareKey::Char('t')).with_alt_modifier(),
|
||||||
Key::Ctrl('a'),
|
KeyWithModifier::new(BareKey::Char('z')).with_alt_modifier(),
|
||||||
Key::Ctrl('1'),
|
|
||||||
];
|
];
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
let ret = get_common_modifiers(keyvec.iter().collect());
|
||||||
assert_eq!(ret, None);
|
assert_eq!(ret, vec![]); // no common modifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn common_modifier_with_any_keys() {
|
fn common_modifier_with_any_keys() {
|
||||||
let keyvec = vec![Key::Backspace, Key::Char('f'), Key::Down];
|
let keyvec = vec![
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
KeyWithModifier::new(BareKey::Char('1')),
|
||||||
assert_eq!(ret, None);
|
KeyWithModifier::new(BareKey::Char('t')).with_alt_modifier(),
|
||||||
}
|
KeyWithModifier::new(BareKey::Char('z')).with_alt_modifier(),
|
||||||
|
];
|
||||||
#[test]
|
let ret = get_common_modifiers(keyvec.iter().collect());
|
||||||
fn common_modifier_with_ctrl_and_normal_keys() {
|
assert_eq!(ret, vec![]); // no common modifiers
|
||||||
let keyvec = vec![Key::Ctrl('a'), Key::Char('f'), Key::Down];
|
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
|
||||||
assert_eq!(ret, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn common_modifier_with_alt_and_normal_keys() {
|
|
||||||
let keyvec = vec![Key::Alt(CharOrArrow::Char('a')), Key::Char('f'), Key::Down];
|
|
||||||
let ret = get_common_modifier(keyvec.iter().collect());
|
|
||||||
assert_eq!(ret, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn action_key_simple_pattern_match_exact() {
|
fn action_key_simple_pattern_match_exact() {
|
||||||
let keymap = &[(Key::Char('f'), vec![Action::Quit])];
|
let keymap = &[(KeyWithModifier::new(BareKey::Char('f')), vec![Action::Quit])];
|
||||||
let ret = action_key(keymap, &[Action::Quit]);
|
let ret = action_key(keymap, &[Action::Quit]);
|
||||||
assert_eq!(ret, vec![Key::Char('f')]);
|
assert_eq!(ret, vec![KeyWithModifier::new(BareKey::Char('f'))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn action_key_simple_pattern_match_pattern_too_long() {
|
fn action_key_simple_pattern_match_pattern_too_long() {
|
||||||
let keymap = &[(Key::Char('f'), vec![Action::Quit])];
|
let keymap = &[(KeyWithModifier::new(BareKey::Char('f')), vec![Action::Quit])];
|
||||||
let ret = action_key(keymap, &[Action::Quit, Action::ScrollUp]);
|
let ret = action_key(keymap, &[Action::Quit, Action::ScrollUp]);
|
||||||
assert_eq!(ret, Vec::new());
|
assert_eq!(ret, Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn action_key_simple_pattern_match_pattern_empty() {
|
fn action_key_simple_pattern_match_pattern_empty() {
|
||||||
let keymap = &[(Key::Char('f'), vec![Action::Quit])];
|
let keymap = &[(KeyWithModifier::new(BareKey::Char('f')), vec![Action::Quit])];
|
||||||
let ret = action_key(keymap, &[]);
|
let ret = action_key(keymap, &[]);
|
||||||
assert_eq!(ret, Vec::new());
|
assert_eq!(ret, Vec::new());
|
||||||
}
|
}
|
||||||
|
|
@ -655,7 +634,10 @@ pub mod tests {
|
||||||
fn action_key_long_pattern_match_exact() {
|
fn action_key_long_pattern_match_exact() {
|
||||||
let keymap = big_keymap();
|
let keymap = big_keymap();
|
||||||
let ret = action_key(&keymap, &[Action::ScrollDown, TO_NORMAL]);
|
let ret = action_key(&keymap, &[Action::ScrollDown, TO_NORMAL]);
|
||||||
assert_eq!(ret, vec![Key::Alt(CharOrArrow::Char('c'))]);
|
assert_eq!(
|
||||||
|
ret,
|
||||||
|
vec![KeyWithModifier::new(BareKey::Char('c')).with_alt_modifier()]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -669,7 +651,7 @@ pub mod tests {
|
||||||
fn action_key_group_single_pattern() {
|
fn action_key_group_single_pattern() {
|
||||||
let keymap = big_keymap();
|
let keymap = big_keymap();
|
||||||
let ret = action_key_group(&keymap, &[&[Action::Quit]]);
|
let ret = action_key_group(&keymap, &[&[Action::Quit]]);
|
||||||
assert_eq!(ret, vec![Key::Char('a')]);
|
assert_eq!(ret, vec![KeyWithModifier::new(BareKey::Char('a'))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -677,7 +659,13 @@ pub mod tests {
|
||||||
let keymap = big_keymap();
|
let keymap = big_keymap();
|
||||||
let ret = action_key_group(&keymap, &[&[Action::ScrollDown], &[Action::ScrollUp]]);
|
let ret = action_key_group(&keymap, &[&[Action::ScrollDown], &[Action::ScrollUp]]);
|
||||||
// Mind the order!
|
// Mind the order!
|
||||||
assert_eq!(ret, vec![Key::Ctrl('d'), Key::Ctrl('b')]);
|
assert_eq!(
|
||||||
|
ret,
|
||||||
|
vec![
|
||||||
|
KeyWithModifier::new(BareKey::Char('d')).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier()
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_palette() -> Palette {
|
fn get_palette() -> Palette {
|
||||||
|
|
@ -686,7 +674,11 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_only_chars() {
|
fn style_key_with_modifier_only_chars() {
|
||||||
let keyvec = vec![Key::Char('a'), Key::Char('b'), Key::Char('c')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Char('a')),
|
||||||
|
KeyWithModifier::new(BareKey::Char('b')),
|
||||||
|
KeyWithModifier::new(BareKey::Char('c')),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||||
|
|
@ -698,10 +690,10 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_special_group_hjkl() {
|
fn style_key_with_modifier_special_group_hjkl() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Char('h'),
|
KeyWithModifier::new(BareKey::Char('h')),
|
||||||
Key::Char('j'),
|
KeyWithModifier::new(BareKey::Char('j')),
|
||||||
Key::Char('k'),
|
KeyWithModifier::new(BareKey::Char('k')),
|
||||||
Key::Char('l'),
|
KeyWithModifier::new(BareKey::Char('l')),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
@ -711,30 +703,13 @@ pub mod tests {
|
||||||
assert_eq!(ret, "<hjkl>".to_string())
|
assert_eq!(ret, "<hjkl>".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn style_key_with_modifier_special_group_hjkl_broken() {
|
|
||||||
// Sorted the wrong way
|
|
||||||
let keyvec = vec![
|
|
||||||
Key::Char('h'),
|
|
||||||
Key::Char('k'),
|
|
||||||
Key::Char('j'),
|
|
||||||
Key::Char('l'),
|
|
||||||
];
|
|
||||||
let palette = get_palette();
|
|
||||||
|
|
||||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
|
||||||
let ret = unstyle(&ANSIStrings(&ret));
|
|
||||||
|
|
||||||
assert_eq!(ret, "<h|k|j|l>".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_special_group_all_arrows() {
|
fn style_key_with_modifier_special_group_all_arrows() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Char('←'),
|
KeyWithModifier::new(BareKey::Left),
|
||||||
Key::Char('↓'),
|
KeyWithModifier::new(BareKey::Down),
|
||||||
Key::Char('↑'),
|
KeyWithModifier::new(BareKey::Up),
|
||||||
Key::Char('→'),
|
KeyWithModifier::new(BareKey::Right),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
@ -746,7 +721,10 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_special_group_left_right_arrows() {
|
fn style_key_with_modifier_special_group_left_right_arrows() {
|
||||||
let keyvec = vec![Key::Char('←'), Key::Char('→')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Left),
|
||||||
|
KeyWithModifier::new(BareKey::Right),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||||
|
|
@ -757,7 +735,10 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_special_group_down_up_arrows() {
|
fn style_key_with_modifier_special_group_down_up_arrows() {
|
||||||
let keyvec = vec![Key::Char('↓'), Key::Char('↑')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Down),
|
||||||
|
KeyWithModifier::new(BareKey::Up),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||||
|
|
@ -769,10 +750,10 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_common_ctrl_modifier_chars() {
|
fn style_key_with_modifier_common_ctrl_modifier_chars() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Ctrl('a'),
|
KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(),
|
||||||
Key::Ctrl('b'),
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
Key::Ctrl('c'),
|
KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier(),
|
||||||
Key::Ctrl('d'),
|
KeyWithModifier::new(BareKey::Char('d')).with_ctrl_modifier(),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
@ -785,10 +766,10 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_common_alt_modifier_chars() {
|
fn style_key_with_modifier_common_alt_modifier_chars() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Alt(CharOrArrow::Char('a')),
|
KeyWithModifier::new(BareKey::Char('a')).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('b')),
|
KeyWithModifier::new(BareKey::Char('b')).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('c')),
|
KeyWithModifier::new(BareKey::Char('c')).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('d')),
|
KeyWithModifier::new(BareKey::Char('d')).with_alt_modifier(),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
@ -801,10 +782,10 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_common_alt_modifier_with_special_group_all_arrows() {
|
fn style_key_with_modifier_common_alt_modifier_with_special_group_all_arrows() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Left)),
|
KeyWithModifier::new(BareKey::Left).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Down)),
|
KeyWithModifier::new(BareKey::Down).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Up)),
|
KeyWithModifier::new(BareKey::Up).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Right)),
|
KeyWithModifier::new(BareKey::Right).with_alt_modifier(),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
@ -817,32 +798,32 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_ctrl_alt_char_mixed() {
|
fn style_key_with_modifier_ctrl_alt_char_mixed() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Alt(CharOrArrow::Char('a')),
|
KeyWithModifier::new(BareKey::Char('a')).with_alt_modifier(),
|
||||||
Key::Ctrl('b'),
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
Key::Char('c'),
|
KeyWithModifier::new(BareKey::Char('c')),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||||
let ret = unstyle(&ANSIStrings(&ret));
|
let ret = unstyle(&ANSIStrings(&ret));
|
||||||
|
|
||||||
assert_eq!(ret, "<Alt+a|Ctrl+b|c>".to_string())
|
assert_eq!(ret, "<Alt a|Ctrl b|c>".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_unprintables() {
|
fn style_key_with_modifier_unprintables() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Backspace,
|
KeyWithModifier::new(BareKey::Backspace),
|
||||||
Key::Char('\n'),
|
KeyWithModifier::new(BareKey::Enter),
|
||||||
Key::Char(' '),
|
KeyWithModifier::new(BareKey::Char(' ')),
|
||||||
Key::Char('\t'),
|
KeyWithModifier::new(BareKey::Tab),
|
||||||
Key::PageDown,
|
KeyWithModifier::new(BareKey::PageDown),
|
||||||
Key::Delete,
|
KeyWithModifier::new(BareKey::Delete),
|
||||||
Key::Home,
|
KeyWithModifier::new(BareKey::Home),
|
||||||
Key::End,
|
KeyWithModifier::new(BareKey::End),
|
||||||
Key::Insert,
|
KeyWithModifier::new(BareKey::Insert),
|
||||||
Key::BackTab,
|
KeyWithModifier::new(BareKey::Tab),
|
||||||
Key::Esc,
|
KeyWithModifier::new(BareKey::Esc),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
@ -857,7 +838,11 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_unprintables_with_common_ctrl_modifier() {
|
fn style_key_with_modifier_unprintables_with_common_ctrl_modifier() {
|
||||||
let keyvec = vec![Key::Ctrl('\n'), Key::Ctrl(' '), Key::Ctrl('\t')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Enter).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Char(' ')).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Tab).with_ctrl_modifier(),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||||
|
|
@ -869,9 +854,9 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn style_key_with_modifier_unprintables_with_common_alt_modifier() {
|
fn style_key_with_modifier_unprintables_with_common_alt_modifier() {
|
||||||
let keyvec = vec![
|
let keyvec = vec![
|
||||||
Key::Alt(CharOrArrow::Char('\n')),
|
KeyWithModifier::new(BareKey::Enter).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char(' ')),
|
KeyWithModifier::new(BareKey::Char(' ')).with_alt_modifier(),
|
||||||
Key::Alt(CharOrArrow::Char('\t')),
|
KeyWithModifier::new(BareKey::Tab).with_alt_modifier(),
|
||||||
];
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
|
|
||||||
fn full_length_shortcut(
|
fn full_length_shortcut(
|
||||||
is_first_shortcut: bool,
|
is_first_shortcut: bool,
|
||||||
key: Vec<Key>,
|
key: Vec<KeyWithModifier>,
|
||||||
action: &str,
|
action: &str,
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
) -> LinePart {
|
) -> LinePart {
|
||||||
|
|
@ -59,7 +59,12 @@ fn locked_interface_indication(palette: Palette) -> LinePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_shortcut(help: &ModeInfo, linepart: &LinePart, text: &str, keys: Vec<Key>) -> LinePart {
|
fn add_shortcut(
|
||||||
|
help: &ModeInfo,
|
||||||
|
linepart: &LinePart,
|
||||||
|
text: &str,
|
||||||
|
keys: Vec<KeyWithModifier>,
|
||||||
|
) -> LinePart {
|
||||||
let shortcut = if linepart.len == 0 {
|
let shortcut = if linepart.len == 0 {
|
||||||
full_length_shortcut(true, keys, text, help.style.colors)
|
full_length_shortcut(true, keys, text, help.style.colors)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -106,7 +111,7 @@ fn full_shortcut_list_nonstandard_mode(help: &ModeInfo) -> LinePart {
|
||||||
// three times the length and all the keybinding vectors we generate become virtually unreadable
|
// three times the length and all the keybinding vectors we generate become virtually unreadable
|
||||||
// for humans.
|
// for humans.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier>)> {
|
||||||
use Action as A;
|
use Action as A;
|
||||||
use InputMode as IM;
|
use InputMode as IM;
|
||||||
use Direction as Dir;
|
use Direction as Dir;
|
||||||
|
|
@ -119,8 +124,8 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
||||||
// Find a keybinding to get back to "Normal" input mode. In this case we prefer '\n' over other
|
// Find a keybinding to get back to "Normal" input mode. In this case we prefer '\n' over other
|
||||||
// choices. Do it here before we dedupe the keymap below!
|
// choices. Do it here before we dedupe the keymap below!
|
||||||
let to_normal_keys = action_key(&old_keymap, &[TO_NORMAL]);
|
let to_normal_keys = action_key(&old_keymap, &[TO_NORMAL]);
|
||||||
let to_normal_key = if to_normal_keys.contains(&Key::Char('\n')) {
|
let to_normal_key = if to_normal_keys.contains(&KeyWithModifier::new(BareKey::Enter)) {
|
||||||
vec![Key::Char('\n')]
|
vec![KeyWithModifier::new(BareKey::Enter)]
|
||||||
} else {
|
} else {
|
||||||
// Yield `vec![key]` if `to_normal_keys` has at least one key, or an empty vec otherwise.
|
// Yield `vec![key]` if `to_normal_keys` has at least one key, or an empty vec otherwise.
|
||||||
to_normal_keys.into_iter().take(1).collect()
|
to_normal_keys.into_iter().take(1).collect()
|
||||||
|
|
@ -164,11 +169,11 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
||||||
// RightArrow.
|
// RightArrow.
|
||||||
// FIXME: So for lack of a better idea we just check this case manually here.
|
// FIXME: So for lack of a better idea we just check this case manually here.
|
||||||
let old_keymap = mi.get_mode_keybinds();
|
let old_keymap = mi.get_mode_keybinds();
|
||||||
let focus_keys_full: Vec<Key> = action_key_group(&old_keymap,
|
let focus_keys_full: Vec<KeyWithModifier> = action_key_group(&old_keymap,
|
||||||
&[&[A::GoToPreviousTab], &[A::GoToNextTab]]);
|
&[&[A::GoToPreviousTab], &[A::GoToNextTab]]);
|
||||||
let focus_keys = if focus_keys_full.contains(&Key::Left)
|
let focus_keys = if focus_keys_full.contains(&KeyWithModifier::new(BareKey::Left))
|
||||||
&& focus_keys_full.contains(&Key::Right) {
|
&& focus_keys_full.contains(&KeyWithModifier::new(BareKey::Right)) {
|
||||||
vec![Key::Left, Key::Right]
|
vec![KeyWithModifier::new(BareKey::Left), KeyWithModifier::new(BareKey::Right)]
|
||||||
} else {
|
} else {
|
||||||
action_key_group(&km, &[&[A::GoToPreviousTab], &[A::GoToNextTab]])
|
action_key_group(&km, &[&[A::GoToPreviousTab], &[A::GoToNextTab]])
|
||||||
};
|
};
|
||||||
|
|
@ -429,7 +434,7 @@ pub fn floating_panes_are_visible(mode_info: &ModeInfo) -> LinePart {
|
||||||
"{}",
|
"{}",
|
||||||
action_key(km, &[Action::SwitchToMode(InputMode::Pane)])
|
action_key(km, &[Action::SwitchToMode(InputMode::Pane)])
|
||||||
.first()
|
.first()
|
||||||
.unwrap_or(&Key::Char('?'))
|
.unwrap_or(&KeyWithModifier::new(BareKey::Char('?')))
|
||||||
);
|
);
|
||||||
let plus = ", ";
|
let plus = ", ";
|
||||||
let p_left_separator = "<";
|
let p_left_separator = "<";
|
||||||
|
|
@ -440,7 +445,7 @@ pub fn floating_panes_are_visible(mode_info: &ModeInfo) -> LinePart {
|
||||||
&[Action::ToggleFloatingPanes, TO_NORMAL]
|
&[Action::ToggleFloatingPanes, TO_NORMAL]
|
||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
.unwrap_or(&Key::Char('?'))
|
.unwrap_or(&KeyWithModifier::new(BareKey::Char('?')))
|
||||||
);
|
);
|
||||||
let p_right_separator = "> ";
|
let p_right_separator = "> ";
|
||||||
let to_hide = "to hide.";
|
let to_hide = "to hide.";
|
||||||
|
|
@ -560,7 +565,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_key() {
|
fn full_length_shortcut_with_key() {
|
||||||
let keyvec = vec![Key::Char('a')];
|
let keyvec = vec![KeyWithModifier::new(BareKey::Char('a'))];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -571,7 +576,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_key_first_element() {
|
fn full_length_shortcut_with_key_first_element() {
|
||||||
let keyvec = vec![Key::Char('a')];
|
let keyvec = vec![KeyWithModifier::new(BareKey::Char('a'))];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(true, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(true, keyvec, "Foobar", palette);
|
||||||
|
|
@ -594,7 +599,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_key_unprintable_1() {
|
fn full_length_shortcut_with_key_unprintable_1() {
|
||||||
let keyvec = vec![Key::Char('\n')];
|
let keyvec = vec![KeyWithModifier::new(BareKey::Enter)];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -605,7 +610,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_key_unprintable_2() {
|
fn full_length_shortcut_with_key_unprintable_2() {
|
||||||
let keyvec = vec![Key::Backspace];
|
let keyvec = vec![KeyWithModifier::new(BareKey::Backspace)];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -616,7 +621,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_ctrl_key() {
|
fn full_length_shortcut_with_ctrl_key() {
|
||||||
let keyvec = vec![Key::Ctrl('a')];
|
let keyvec = vec![KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier()];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -627,7 +632,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_alt_key() {
|
fn full_length_shortcut_with_alt_key() {
|
||||||
let keyvec = vec![Key::Alt(CharOrArrow::Char('a'))];
|
let keyvec = vec![KeyWithModifier::new(BareKey::Char('a')).with_alt_modifier()];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -638,7 +643,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_homogenous_key_group() {
|
fn full_length_shortcut_with_homogenous_key_group() {
|
||||||
let keyvec = vec![Key::Char('a'), Key::Char('b'), Key::Char('c')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Char('a')),
|
||||||
|
KeyWithModifier::new(BareKey::Char('b')),
|
||||||
|
KeyWithModifier::new(BareKey::Char('c')),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -649,18 +658,26 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_heterogenous_key_group() {
|
fn full_length_shortcut_with_heterogenous_key_group() {
|
||||||
let keyvec = vec![Key::Char('a'), Key::Ctrl('b'), Key::Char('\n')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Char('a')),
|
||||||
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Enter),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
let ret = unstyle(ret);
|
let ret = unstyle(ret);
|
||||||
|
|
||||||
assert_eq!(ret, " / <a|Ctrl+b|ENTER> Foobar");
|
assert_eq!(ret, " / <a|Ctrl b|ENTER> Foobar");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_length_shortcut_with_key_group_shared_ctrl_modifier() {
|
fn full_length_shortcut_with_key_group_shared_ctrl_modifier() {
|
||||||
let keyvec = vec![Key::Ctrl('a'), Key::Ctrl('b'), Key::Ctrl('c')];
|
let keyvec = vec![
|
||||||
|
KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
|
KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier(),
|
||||||
|
];
|
||||||
let palette = get_palette();
|
let palette = get_palette();
|
||||||
|
|
||||||
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
let ret = full_length_shortcut(false, keyvec, "Foobar", palette);
|
||||||
|
|
@ -678,14 +695,32 @@ mod tests {
|
||||||
keybinds: vec![(
|
keybinds: vec![(
|
||||||
InputMode::Pane,
|
InputMode::Pane,
|
||||||
vec![
|
vec![
|
||||||
(Key::Left, vec![Action::MoveFocus(Direction::Left)]),
|
|
||||||
(Key::Down, vec![Action::MoveFocus(Direction::Down)]),
|
|
||||||
(Key::Up, vec![Action::MoveFocus(Direction::Up)]),
|
|
||||||
(Key::Right, vec![Action::MoveFocus(Direction::Right)]),
|
|
||||||
(Key::Char('n'), vec![Action::NewPane(None, None), TO_NORMAL]),
|
|
||||||
(Key::Char('x'), vec![Action::CloseFocus, TO_NORMAL]),
|
|
||||||
(
|
(
|
||||||
Key::Char('f'),
|
KeyWithModifier::new(BareKey::Left),
|
||||||
|
vec![Action::MoveFocus(Direction::Left)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Down),
|
||||||
|
vec![Action::MoveFocus(Direction::Down)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Up),
|
||||||
|
vec![Action::MoveFocus(Direction::Up)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Right),
|
||||||
|
vec![Action::MoveFocus(Direction::Right)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('n')),
|
||||||
|
vec![Action::NewPane(None, None), TO_NORMAL],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('x')),
|
||||||
|
vec![Action::CloseFocus, TO_NORMAL],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('f')),
|
||||||
vec![Action::ToggleFocusFullscreen, TO_NORMAL],
|
vec![Action::ToggleFocusFullscreen, TO_NORMAL],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -710,14 +745,32 @@ mod tests {
|
||||||
keybinds: vec![(
|
keybinds: vec![(
|
||||||
InputMode::Pane,
|
InputMode::Pane,
|
||||||
vec![
|
vec![
|
||||||
(Key::Left, vec![Action::MoveFocus(Direction::Left)]),
|
|
||||||
(Key::Down, vec![Action::MoveFocus(Direction::Down)]),
|
|
||||||
(Key::Up, vec![Action::MoveFocus(Direction::Up)]),
|
|
||||||
(Key::Right, vec![Action::MoveFocus(Direction::Right)]),
|
|
||||||
(Key::Char('n'), vec![Action::NewPane(None, None), TO_NORMAL]),
|
|
||||||
(Key::Char('x'), vec![Action::CloseFocus, TO_NORMAL]),
|
|
||||||
(
|
(
|
||||||
Key::Char('f'),
|
KeyWithModifier::new(BareKey::Left),
|
||||||
|
vec![Action::MoveFocus(Direction::Left)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Down),
|
||||||
|
vec![Action::MoveFocus(Direction::Down)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Up),
|
||||||
|
vec![Action::MoveFocus(Direction::Up)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Right),
|
||||||
|
vec![Action::MoveFocus(Direction::Right)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('n')),
|
||||||
|
vec![Action::NewPane(None, None), TO_NORMAL],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('x')),
|
||||||
|
vec![Action::CloseFocus, TO_NORMAL],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('f')),
|
||||||
vec![Action::ToggleFocusFullscreen, TO_NORMAL],
|
vec![Action::ToggleFocusFullscreen, TO_NORMAL],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -738,13 +791,34 @@ mod tests {
|
||||||
keybinds: vec![(
|
keybinds: vec![(
|
||||||
InputMode::Pane,
|
InputMode::Pane,
|
||||||
vec![
|
vec![
|
||||||
(Key::Ctrl('a'), vec![Action::MoveFocus(Direction::Left)]),
|
(
|
||||||
(Key::Ctrl('\n'), vec![Action::MoveFocus(Direction::Down)]),
|
KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier(),
|
||||||
(Key::Ctrl('1'), vec![Action::MoveFocus(Direction::Up)]),
|
vec![Action::MoveFocus(Direction::Left)],
|
||||||
(Key::Ctrl(' '), vec![Action::MoveFocus(Direction::Right)]),
|
),
|
||||||
(Key::Backspace, vec![Action::NewPane(None, None), TO_NORMAL]),
|
(
|
||||||
(Key::Esc, vec![Action::CloseFocus, TO_NORMAL]),
|
KeyWithModifier::new(BareKey::Enter).with_ctrl_modifier(),
|
||||||
(Key::End, vec![Action::ToggleFocusFullscreen, TO_NORMAL]),
|
vec![Action::MoveFocus(Direction::Down)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier(),
|
||||||
|
vec![Action::MoveFocus(Direction::Up)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char(' ')).with_ctrl_modifier(),
|
||||||
|
vec![Action::MoveFocus(Direction::Right)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Backspace),
|
||||||
|
vec![Action::NewPane(None, None), TO_NORMAL],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Esc),
|
||||||
|
vec![Action::CloseFocus, TO_NORMAL],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
KeyWithModifier::new(BareKey::End),
|
||||||
|
vec![Action::ToggleFocusFullscreen, TO_NORMAL],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)],
|
)],
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,10 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups {
|
||||||
&[Action::Resize(Resize::Decrease, None)],
|
&[Action::Resize(Resize::Decrease, None)],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if resize_keys.contains(&Key::Alt(CharOrArrow::Char('=')))
|
if resize_keys.contains(&KeyWithModifier::new(BareKey::Char('=')).with_alt_modifier())
|
||||||
&& resize_keys.contains(&Key::Alt(CharOrArrow::Char('+')))
|
&& resize_keys.contains(&KeyWithModifier::new(BareKey::Char('+')).with_alt_modifier())
|
||||||
{
|
{
|
||||||
resize_keys.retain(|k| k != &Key::Alt(CharOrArrow::Char('=')));
|
resize_keys.retain(|k| k != &KeyWithModifier::new(BareKey::Char('=')).with_alt_modifier())
|
||||||
}
|
}
|
||||||
let resize = if resize_keys.is_empty() {
|
let resize = if resize_keys.is_empty() {
|
||||||
vec![Style::new().bold().paint("UNBOUND")]
|
vec![Style::new().bold().paint("UNBOUND")]
|
||||||
|
|
|
||||||
|
|
@ -63,42 +63,47 @@ impl ZellijPlugin for State {
|
||||||
self.update_files(paths);
|
self.update_files(paths);
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Event::Key(key) => match key {
|
Event::Key(key) => match key.bare_key {
|
||||||
Key::Char(character) if character != '\n' => {
|
BareKey::Char(character) if key.has_no_modifiers() => {
|
||||||
self.update_search_term(character);
|
self.update_search_term(character);
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Backspace => {
|
BareKey::Backspace if key.has_no_modifiers() => {
|
||||||
self.handle_backspace();
|
self.handle_backspace();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Esc | Key::Ctrl('c') => {
|
BareKey::Esc if key.has_no_modifiers() => {
|
||||||
self.clear_search_term_or_descend();
|
self.clear_search_term_or_descend();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Up => {
|
BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
|
self.clear_search_term_or_descend();
|
||||||
|
},
|
||||||
|
BareKey::Up if key.has_no_modifiers() => {
|
||||||
self.move_selection_up();
|
self.move_selection_up();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Down => {
|
BareKey::Down if key.has_no_modifiers() => {
|
||||||
self.move_selection_down();
|
self.move_selection_down();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Char('\n') if self.handling_filepick_request_from.is_some() => {
|
BareKey::Enter
|
||||||
|
if key.has_no_modifiers() && self.handling_filepick_request_from.is_some() =>
|
||||||
|
{
|
||||||
self.send_filepick_response();
|
self.send_filepick_response();
|
||||||
},
|
},
|
||||||
Key::Char('\n') => {
|
BareKey::Enter if key.has_no_modifiers() => {
|
||||||
self.open_selected_path();
|
self.open_selected_path();
|
||||||
},
|
},
|
||||||
Key::Right | Key::BackTab => {
|
BareKey::Right | BareKey::Tab if key.has_no_modifiers() => {
|
||||||
self.traverse_dir();
|
self.traverse_dir();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Left => {
|
BareKey::Left if key.has_no_modifiers() => {
|
||||||
self.descend_to_previous_path();
|
self.descend_to_previous_path();
|
||||||
should_render = true;
|
should_render = true;
|
||||||
},
|
},
|
||||||
Key::Ctrl('e') => {
|
BareKey::Char('e') if key.has_modifiers(&[KeyModifier::Ctrl]) => {
|
||||||
should_render = true;
|
should_render = true;
|
||||||
self.toggle_hidden_files();
|
self.toggle_hidden_files();
|
||||||
refresh_directory(&self.file_list_view.path);
|
refresh_directory(&self.file_list_view.path);
|
||||||
|
|
|
||||||
|
|
@ -2226,6 +2226,7 @@ pub fn send_command_through_the_cli() {
|
||||||
// cursor does not appear in
|
// cursor does not appear in
|
||||||
// suspend_start panes
|
// suspend_start panes
|
||||||
{
|
{
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
remote_terminal.send_key(&SPACE); // run script - here we use SPACE
|
remote_terminal.send_key(&SPACE); // run script - here we use SPACE
|
||||||
// instead of the default ENTER because
|
// instead of the default ENTER because
|
||||||
// sending ENTER over SSH can be a little
|
// sending ENTER over SSH can be a little
|
||||||
|
|
@ -2243,6 +2244,7 @@ pub fn send_command_through_the_cli() {
|
||||||
if remote_terminal.snapshot_contains("<Ctrl-c>")
|
if remote_terminal.snapshot_contains("<Ctrl-c>")
|
||||||
&& remote_terminal.cursor_position_is(76, 3)
|
&& remote_terminal.cursor_position_is(76, 3)
|
||||||
{
|
{
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
remote_terminal.send_key(&SPACE); // re-run script - here we use SPACE
|
remote_terminal.send_key(&SPACE); // re-run script - here we use SPACE
|
||||||
// instead of the default ENTER because
|
// instead of the default ENTER because
|
||||||
// sending ENTER over SSH can be a little
|
// sending ENTER over SSH can be a little
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ fn read_from_channel(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_output = TerminalPane::new(
|
let mut terminal_output = TerminalPane::new(
|
||||||
0,
|
0,
|
||||||
pane_geom,
|
pane_geom,
|
||||||
|
|
@ -253,6 +254,7 @@ fn read_from_channel(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
loop {
|
loop {
|
||||||
if !should_keep_running.load(Ordering::SeqCst) {
|
if !should_keep_running.load(Ordering::SeqCst) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
assertion_line: 1048
|
assertion_line: 1171
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
---
|
---
|
||||||
Zellij (e2e-test) Tab #1 Tab #2 Tab #3 Tab #4
|
Zellij (e2e-test) Tab #1 Tab #2 Tab #3 Tab #4
|
||||||
|
|
@ -26,4 +26,4 @@ expression: last_snapshot
|
||||||
│ ││ │
|
│ ││ │
|
||||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||||
(FLOATING PANES VISIBLE): Press Ctrl+p, <w> to hide.
|
(FLOATING PANES VISIBLE): Press Ctrl p, <w> to hide.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
assertion_line: 1109
|
assertion_line: 1232
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
---
|
---
|
||||||
Zellij (e2e-test) Tab #1 Tab #2 Tab #3 Tab #4
|
Zellij (e2e-test) Tab #1 Tab #2 Tab #3 Tab #4
|
||||||
|
|
@ -26,4 +26,4 @@ expression: last_snapshot
|
||||||
│ ││ │
|
│ ││ │
|
||||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||||
(FLOATING PANES VISIBLE): Press Ctrl+p, <w> to hide.
|
(FLOATING PANES VISIBLE): Press Ctrl p, <w> to hide.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
assertion_line: 1011
|
assertion_line: 1266
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
---
|
---
|
||||||
Zellij (e2e-test) Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
|
|
@ -25,5 +25,5 @@ expression: last_snapshot
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
<F1> LOCK <F2> PANE <F3> TAB <F4> RESIZE <F5> MOVE <F6> SEARCH <Alt+F7> SESSION <Ctrl+F8> QUIT
|
<F1> LOCK <F2> PANE <F3> TAB <F4> RESIZE <F5> MOVE <F6> SEARCH <Alt F7> SESSION <Ctrl F8> QUIT
|
||||||
Tip: UNBOUND => open new pane. UNBOUND => navigate between panes. UNBOUND => increase/decrease pane size.
|
Tip: UNBOUND => open new pane. UNBOUND => navigate between panes. UNBOUND => increase/decrease pane size.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
assertion_line: 1724
|
assertion_line: 1984
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
---
|
---
|
||||||
Zellij (e2e-test) Tab #1
|
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
|
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||||
(FLOATING PANES VISIBLE): Press Ctrl+p, <w> to hide.
|
(FLOATING PANES VISIBLE): Press Ctrl p, <w> to hide.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
channels::{Receiver, SenderWithContext, OPENCALLS},
|
channels::{Receiver, SenderWithContext, OPENCALLS},
|
||||||
data::{InputMode, Key},
|
data::{InputMode, KeyWithModifier},
|
||||||
errors::{ContextType, ErrorContext, FatalError},
|
errors::{ContextType, ErrorContext, FatalError},
|
||||||
input::{
|
input::{
|
||||||
actions::Action,
|
actions::Action,
|
||||||
|
|
@ -96,7 +96,7 @@ impl InputHandler {
|
||||||
&raw_bytes,
|
&raw_bytes,
|
||||||
Some((&self.config.keybinds, &self.mode)),
|
Some((&self.config.keybinds, &self.mode)),
|
||||||
);
|
);
|
||||||
self.handle_key(&key, raw_bytes);
|
self.handle_key(&key, raw_bytes, false);
|
||||||
},
|
},
|
||||||
InputEvent::Mouse(mouse_event) => {
|
InputEvent::Mouse(mouse_event) => {
|
||||||
let mouse_event =
|
let mouse_event =
|
||||||
|
|
@ -106,15 +106,15 @@ impl InputHandler {
|
||||||
InputEvent::Paste(pasted_text) => {
|
InputEvent::Paste(pasted_text) => {
|
||||||
if self.mode == InputMode::Normal || self.mode == InputMode::Locked {
|
if self.mode == InputMode::Normal || self.mode == InputMode::Locked {
|
||||||
self.dispatch_action(
|
self.dispatch_action(
|
||||||
Action::Write(bracketed_paste_start.clone()),
|
Action::Write(None, bracketed_paste_start.clone(), false),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
self.dispatch_action(
|
self.dispatch_action(
|
||||||
Action::Write(pasted_text.as_bytes().to_vec()),
|
Action::Write(None, pasted_text.as_bytes().to_vec(), false),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
self.dispatch_action(
|
self.dispatch_action(
|
||||||
Action::Write(bracketed_paste_end.clone()),
|
Action::Write(None, bracketed_paste_end.clone(), false),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +140,12 @@ impl InputHandler {
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Ok((
|
||||||
|
InputInstruction::KeyWithModifierEvent(key_with_modifier, raw_bytes),
|
||||||
|
_error_context,
|
||||||
|
)) => {
|
||||||
|
self.handle_key(&key_with_modifier, raw_bytes, true);
|
||||||
|
},
|
||||||
Ok((InputInstruction::SwitchToMode(input_mode), _error_context)) => {
|
Ok((InputInstruction::SwitchToMode(input_mode), _error_context)) => {
|
||||||
self.mode = input_mode;
|
self.mode = input_mode;
|
||||||
},
|
},
|
||||||
|
|
@ -168,11 +174,19 @@ impl InputHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) {
|
fn handle_key(
|
||||||
|
&mut self,
|
||||||
|
key: &KeyWithModifier,
|
||||||
|
raw_bytes: Vec<u8>,
|
||||||
|
is_kitty_keyboard_protocol: bool,
|
||||||
|
) {
|
||||||
let keybinds = &self.config.keybinds;
|
let keybinds = &self.config.keybinds;
|
||||||
for action in
|
for action in keybinds.get_actions_for_key_in_mode_or_default_action(
|
||||||
keybinds.get_actions_for_key_in_mode_or_default_action(&self.mode, key, raw_bytes)
|
&self.mode,
|
||||||
{
|
key,
|
||||||
|
raw_bytes,
|
||||||
|
is_kitty_keyboard_protocol,
|
||||||
|
) {
|
||||||
let should_exit = self.dispatch_action(action, None);
|
let should_exit = self.dispatch_action(action, None);
|
||||||
if should_exit {
|
if should_exit {
|
||||||
self.should_exit = true;
|
self.should_exit = true;
|
||||||
|
|
|
||||||
1779
zellij-client/src/keyboard_parser.rs
Normal file
1779
zellij-client/src/keyboard_parser.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@ pub mod os_input_output;
|
||||||
pub mod cli_client;
|
pub mod cli_client;
|
||||||
mod command_is_executing;
|
mod command_is_executing;
|
||||||
mod input_handler;
|
mod input_handler;
|
||||||
|
mod keyboard_parser;
|
||||||
pub mod old_config_converter;
|
pub mod old_config_converter;
|
||||||
mod stdin_ansi_parser;
|
mod stdin_ansi_parser;
|
||||||
mod stdin_handler;
|
mod stdin_handler;
|
||||||
|
|
@ -24,7 +25,7 @@ use crate::{
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
channels::{self, ChannelWithContext, SenderWithContext},
|
channels::{self, ChannelWithContext, SenderWithContext},
|
||||||
consts::{set_permissions, ZELLIJ_SOCK_DIR},
|
consts::{set_permissions, ZELLIJ_SOCK_DIR},
|
||||||
data::{ClientId, ConnectToSession, InputMode, Style},
|
data::{ClientId, ConnectToSession, InputMode, KeyWithModifier, Style},
|
||||||
envs,
|
envs,
|
||||||
errors::{ClientContext, ContextType, ErrorInstruction},
|
errors::{ClientContext, ContextType, ErrorInstruction},
|
||||||
input::{config::Config, options::Options},
|
input::{config::Config, options::Options},
|
||||||
|
|
@ -152,6 +153,7 @@ impl ClientInfo {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum InputInstruction {
|
pub(crate) enum InputInstruction {
|
||||||
KeyEvent(InputEvent, Vec<u8>),
|
KeyEvent(InputEvent, Vec<u8>),
|
||||||
|
KeyWithModifierEvent(KeyWithModifier, Vec<u8>),
|
||||||
SwitchToMode(InputMode),
|
SwitchToMode(InputMode),
|
||||||
AnsiStdinInstructions(Vec<AnsiStdinInstruction>),
|
AnsiStdinInstructions(Vec<AnsiStdinInstruction>),
|
||||||
StartedParsing,
|
StartedParsing,
|
||||||
|
|
@ -177,16 +179,21 @@ pub fn start_client(
|
||||||
}
|
}
|
||||||
info!("Starting Zellij client!");
|
info!("Starting Zellij client!");
|
||||||
|
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = config_options
|
||||||
|
.support_kitty_keyboard_protocol
|
||||||
|
.map(|e| !e)
|
||||||
|
.unwrap_or(false);
|
||||||
let mut reconnect_to_session = None;
|
let mut reconnect_to_session = None;
|
||||||
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";
|
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";
|
||||||
let take_snapshot = "\u{1b}[?1049h";
|
let take_snapshot = "\u{1b}[?1049h";
|
||||||
let bracketed_paste = "\u{1b}[?2004h";
|
let bracketed_paste = "\u{1b}[?2004h";
|
||||||
|
let enter_kitty_keyboard_mode = "\u{1b}[>1u";
|
||||||
os_input.unset_raw_mode(0).unwrap();
|
os_input.unset_raw_mode(0).unwrap();
|
||||||
|
|
||||||
if !is_a_reconnect {
|
if !is_a_reconnect {
|
||||||
// we don't do this for a reconnect because our controlling terminal already has the
|
// we don't do this for a reconnect because our controlling terminal already has the
|
||||||
// attributes we want from it, and some terminals don't treat these atomically (looking at
|
// attributes we want from it, and some terminals don't treat these atomically (looking at
|
||||||
// your Windows Terminal...)
|
// you Windows Terminal...)
|
||||||
let _ = os_input
|
let _ = os_input
|
||||||
.get_stdout_writer()
|
.get_stdout_writer()
|
||||||
.write(take_snapshot.as_bytes())
|
.write(take_snapshot.as_bytes())
|
||||||
|
|
@ -195,6 +202,12 @@ pub fn start_client(
|
||||||
.get_stdout_writer()
|
.get_stdout_writer()
|
||||||
.write(clear_client_terminal_attributes.as_bytes())
|
.write(clear_client_terminal_attributes.as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
if !explicitly_disable_kitty_keyboard_protocol {
|
||||||
|
let _ = os_input
|
||||||
|
.get_stdout_writer()
|
||||||
|
.write(enter_kitty_keyboard_mode.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
envs::set_zellij("0".to_string());
|
envs::set_zellij("0".to_string());
|
||||||
config.env.set_vars();
|
config.env.set_vars();
|
||||||
|
|
@ -299,7 +312,14 @@ pub fn start_client(
|
||||||
let os_input = os_input.clone();
|
let os_input = os_input.clone();
|
||||||
let send_input_instructions = send_input_instructions.clone();
|
let send_input_instructions = send_input_instructions.clone();
|
||||||
let stdin_ansi_parser = stdin_ansi_parser.clone();
|
let stdin_ansi_parser = stdin_ansi_parser.clone();
|
||||||
move || stdin_loop(os_input, send_input_instructions, stdin_ansi_parser)
|
move || {
|
||||||
|
stdin_loop(
|
||||||
|
os_input,
|
||||||
|
send_input_instructions,
|
||||||
|
stdin_ansi_parser,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
|
)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let _input_thread = thread::Builder::new()
|
let _input_thread = thread::Builder::new()
|
||||||
|
|
@ -533,6 +553,11 @@ pub fn start_client(
|
||||||
info!("{}", exit_msg);
|
info!("{}", exit_msg);
|
||||||
os_input.unset_raw_mode(0).unwrap();
|
os_input.unset_raw_mode(0).unwrap();
|
||||||
let mut stdout = os_input.get_stdout_writer();
|
let mut stdout = os_input.get_stdout_writer();
|
||||||
|
let exit_kitty_keyboard_mode = "\u{1b}[<1u";
|
||||||
|
if !explicitly_disable_kitty_keyboard_protocol {
|
||||||
|
let _ = stdout.write(exit_kitty_keyboard_mode.as_bytes()).unwrap();
|
||||||
|
stdout.flush().unwrap();
|
||||||
|
}
|
||||||
let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
|
let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
|
||||||
stdout.flush().unwrap();
|
stdout.flush().unwrap();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::keyboard_parser::KittyKeyboardParser;
|
||||||
use crate::os_input_output::ClientOsApi;
|
use crate::os_input_output::ClientOsApi;
|
||||||
use crate::stdin_ansi_parser::StdinAnsiParser;
|
use crate::stdin_ansi_parser::StdinAnsiParser;
|
||||||
use crate::InputInstruction;
|
use crate::InputInstruction;
|
||||||
|
|
@ -23,6 +24,7 @@ pub(crate) fn stdin_loop(
|
||||||
mut os_input: Box<dyn ClientOsApi>,
|
mut os_input: Box<dyn ClientOsApi>,
|
||||||
send_input_instructions: SenderWithContext<InputInstruction>,
|
send_input_instructions: SenderWithContext<InputInstruction>,
|
||||||
stdin_ansi_parser: Arc<Mutex<StdinAnsiParser>>,
|
stdin_ansi_parser: Arc<Mutex<StdinAnsiParser>>,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
) {
|
) {
|
||||||
let mut holding_mouse = false;
|
let mut holding_mouse = false;
|
||||||
let mut input_parser = InputParser::new();
|
let mut input_parser = InputParser::new();
|
||||||
|
|
@ -85,6 +87,24 @@ pub(crate) fn stdin_loop(
|
||||||
.write_cache(ansi_stdin_events.drain(..).collect());
|
.write_cache(ansi_stdin_events.drain(..).collect());
|
||||||
}
|
}
|
||||||
current_buffer.append(&mut buf.to_vec());
|
current_buffer.append(&mut buf.to_vec());
|
||||||
|
|
||||||
|
if !explicitly_disable_kitty_keyboard_protocol {
|
||||||
|
// first we try to parse with the KittyKeyboardParser
|
||||||
|
// if we fail, we try to parse normally
|
||||||
|
match KittyKeyboardParser::new().parse(&buf) {
|
||||||
|
Some(key_with_modifier) => {
|
||||||
|
send_input_instructions
|
||||||
|
.send(InputInstruction::KeyWithModifierEvent(
|
||||||
|
key_with_modifier,
|
||||||
|
current_buffer.drain(..).collect(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let maybe_more = false; // read_from_stdin should (hopefully) always empty the STDIN buffer completely
|
let maybe_more = false; // read_from_stdin should (hopefully) always empty the STDIN buffer completely
|
||||||
let mut events = vec![];
|
let mut events = vec![];
|
||||||
input_parser.parse(
|
input_parser.parse(
|
||||||
|
|
|
||||||
|
|
@ -362,6 +362,9 @@ pub struct Grid {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
pub supports_kitty_keyboard_protocol: bool, // has the app requested kitty keyboard support?
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool, // has kitty keyboard support been explicitly
|
||||||
|
// disabled by user config?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -450,6 +453,7 @@ impl Grid {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let sixel_grid = SixelGrid::new(character_cell_size.clone(), sixel_image_store);
|
let sixel_grid = SixelGrid::new(character_cell_size.clone(), sixel_image_store);
|
||||||
// make sure this is initialized as it is used internally
|
// make sure this is initialized as it is used internally
|
||||||
|
|
@ -505,6 +509,8 @@ impl Grid {
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
lock_renders: false,
|
lock_renders: false,
|
||||||
|
supports_kitty_keyboard_protocol: false,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn render_full_viewport(&mut self) {
|
pub fn render_full_viewport(&mut self) {
|
||||||
|
|
@ -2543,6 +2549,7 @@ impl Perform for Grid {
|
||||||
&mut self.viewport,
|
&mut self.viewport,
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
&mut self.sixel_grid,
|
&mut self.sixel_grid,
|
||||||
|
&mut self.supports_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.alternate_screen_state = None;
|
self.alternate_screen_state = None;
|
||||||
|
|
@ -2636,6 +2643,10 @@ impl Perform for Grid {
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
Cursor::new(0, 0, self.styled_underlines),
|
Cursor::new(0, 0, self.styled_underlines),
|
||||||
);
|
);
|
||||||
|
let current_supports_kitty_keyboard_protocol = std::mem::replace(
|
||||||
|
&mut self.supports_kitty_keyboard_protocol,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let sixel_image_store = self.sixel_grid.sixel_image_store.clone();
|
let sixel_image_store = self.sixel_grid.sixel_image_store.clone();
|
||||||
let alternate_sixelgrid = std::mem::replace(
|
let alternate_sixelgrid = std::mem::replace(
|
||||||
&mut self.sixel_grid,
|
&mut self.sixel_grid,
|
||||||
|
|
@ -2646,6 +2657,7 @@ impl Perform for Grid {
|
||||||
current_viewport,
|
current_viewport,
|
||||||
current_cursor,
|
current_cursor,
|
||||||
alternate_sixelgrid,
|
alternate_sixelgrid,
|
||||||
|
current_supports_kitty_keyboard_protocol,
|
||||||
));
|
));
|
||||||
self.clear_viewport_before_rendering = true;
|
self.clear_viewport_before_rendering = true;
|
||||||
self.scrollback_buffer_lines =
|
self.scrollback_buffer_lines =
|
||||||
|
|
@ -2825,6 +2837,27 @@ impl Perform for Grid {
|
||||||
}
|
}
|
||||||
} else if c == 's' {
|
} else if c == 's' {
|
||||||
self.save_cursor_position();
|
self.save_cursor_position();
|
||||||
|
} else if c == 'u' && intermediates == &[b'>'] {
|
||||||
|
// Zellij only supports the first "progressive enhancement" layer of the kitty keyboard
|
||||||
|
// protocol
|
||||||
|
if !self.explicitly_disable_kitty_keyboard_protocol {
|
||||||
|
self.supports_kitty_keyboard_protocol = true;
|
||||||
|
}
|
||||||
|
} else if c == 'u' && intermediates == &[b'<'] {
|
||||||
|
// Zellij only supports the first "progressive enhancement" layer of the kitty keyboard
|
||||||
|
// protocol
|
||||||
|
if !self.explicitly_disable_kitty_keyboard_protocol {
|
||||||
|
self.supports_kitty_keyboard_protocol = false;
|
||||||
|
}
|
||||||
|
} else if c == 'u' && intermediates == &[b'?'] {
|
||||||
|
// Zellij only supports the first "progressive enhancement" layer of the kitty keyboard
|
||||||
|
// protocol
|
||||||
|
let reply = if self.supports_kitty_keyboard_protocol {
|
||||||
|
"\u{1b}[?1u"
|
||||||
|
} else {
|
||||||
|
"\u{1b}[?0u"
|
||||||
|
};
|
||||||
|
self.pending_messages_to_pty.push(reply.as_bytes().to_vec());
|
||||||
} else if c == 'u' {
|
} else if c == 'u' {
|
||||||
self.restore_cursor_position();
|
self.restore_cursor_position();
|
||||||
} else if c == '@' {
|
} else if c == '@' {
|
||||||
|
|
@ -3084,6 +3117,7 @@ pub struct AlternateScreenState {
|
||||||
viewport: Vec<Row>,
|
viewport: Vec<Row>,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
sixel_grid: SixelGrid,
|
sixel_grid: SixelGrid,
|
||||||
|
supports_kitty_keyboard_protocol: bool,
|
||||||
}
|
}
|
||||||
impl AlternateScreenState {
|
impl AlternateScreenState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
@ -3091,12 +3125,14 @@ impl AlternateScreenState {
|
||||||
viewport: Vec<Row>,
|
viewport: Vec<Row>,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
sixel_grid: SixelGrid,
|
sixel_grid: SixelGrid,
|
||||||
|
supports_kitty_keyboard_protocol: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
AlternateScreenState {
|
AlternateScreenState {
|
||||||
lines_above,
|
lines_above,
|
||||||
viewport,
|
viewport,
|
||||||
cursor,
|
cursor,
|
||||||
sixel_grid,
|
sixel_grid,
|
||||||
|
supports_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn apply_contents_to(
|
pub fn apply_contents_to(
|
||||||
|
|
@ -3105,11 +3141,16 @@ impl AlternateScreenState {
|
||||||
viewport: &mut Vec<Row>,
|
viewport: &mut Vec<Row>,
|
||||||
cursor: &mut Cursor,
|
cursor: &mut Cursor,
|
||||||
sixel_grid: &mut SixelGrid,
|
sixel_grid: &mut SixelGrid,
|
||||||
|
supports_kitty_keyboard_protocol: &mut bool,
|
||||||
) {
|
) {
|
||||||
std::mem::swap(&mut self.lines_above, lines_above);
|
std::mem::swap(&mut self.lines_above, lines_above);
|
||||||
std::mem::swap(&mut self.viewport, viewport);
|
std::mem::swap(&mut self.viewport, viewport);
|
||||||
std::mem::swap(&mut self.cursor, cursor);
|
std::mem::swap(&mut self.cursor, cursor);
|
||||||
std::mem::swap(&mut self.sixel_grid, sixel_grid);
|
std::mem::swap(&mut self.sixel_grid, sixel_grid);
|
||||||
|
std::mem::swap(
|
||||||
|
&mut self.supports_kitty_keyboard_protocol,
|
||||||
|
supports_kitty_keyboard_protocol,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,12 @@ use std::collections::{BTreeSet, HashMap};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::output::{CharacterChunk, SixelImageChunk};
|
use crate::output::{CharacterChunk, SixelImageChunk};
|
||||||
use crate::panes::{grid::Grid, sixel::SixelImageStore, LinkHandler, PaneId};
|
use crate::panes::{
|
||||||
|
grid::Grid,
|
||||||
|
sixel::SixelImageStore,
|
||||||
|
terminal_pane::{BRACKETED_PASTE_BEGIN, BRACKETED_PASTE_END},
|
||||||
|
LinkHandler, PaneId,
|
||||||
|
};
|
||||||
use crate::plugins::PluginInstruction;
|
use crate::plugins::PluginInstruction;
|
||||||
use crate::pty::VteBytes;
|
use crate::pty::VteBytes;
|
||||||
use crate::tab::{AdjustedInput, Pane};
|
use crate::tab::{AdjustedInput, Pane};
|
||||||
|
|
@ -13,7 +18,9 @@ use crate::ui::{
|
||||||
use crate::ClientId;
|
use crate::ClientId;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use zellij_utils::data::{PermissionStatus, PermissionType, PluginPermission};
|
use zellij_utils::data::{
|
||||||
|
BareKey, KeyWithModifier, PermissionStatus, PermissionType, PluginPermission,
|
||||||
|
};
|
||||||
use zellij_utils::pane_size::{Offset, SizeInPixels};
|
use zellij_utils::pane_size::{Offset, SizeInPixels};
|
||||||
use zellij_utils::position::Position;
|
use zellij_utils::position::Position;
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
|
|
@ -39,6 +46,7 @@ macro_rules! get_or_create_grid {
|
||||||
($self:ident, $client_id:ident) => {{
|
($self:ident, $client_id:ident) => {{
|
||||||
let rows = $self.get_content_rows();
|
let rows = $self.get_content_rows();
|
||||||
let cols = $self.get_content_columns();
|
let cols = $self.get_content_columns();
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false; // N/A for plugins
|
||||||
|
|
||||||
$self.grids.entry($client_id).or_insert_with(|| {
|
$self.grids.entry($client_id).or_insert_with(|| {
|
||||||
let mut grid = Grid::new(
|
let mut grid = Grid::new(
|
||||||
|
|
@ -53,6 +61,7 @@ macro_rules! get_or_create_grid {
|
||||||
$self.debug,
|
$self.debug,
|
||||||
$self.arrow_fonts,
|
$self.arrow_fonts,
|
||||||
$self.styled_underlines,
|
$self.styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
grid.hide_cursor();
|
grid.hide_cursor();
|
||||||
grid
|
grid
|
||||||
|
|
@ -232,24 +241,54 @@ impl Pane for PluginPane {
|
||||||
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn adjust_input_to_terminal(&mut self, input_bytes: Vec<u8>) -> Option<AdjustedInput> {
|
fn adjust_input_to_terminal(
|
||||||
|
&mut self,
|
||||||
|
key_with_modifier: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
_raw_input_bytes_are_kitty: bool,
|
||||||
|
) -> Option<AdjustedInput> {
|
||||||
if let Some(requesting_permissions) = &self.requesting_permissions {
|
if let Some(requesting_permissions) = &self.requesting_permissions {
|
||||||
let permissions = requesting_permissions.permissions.clone();
|
let permissions = requesting_permissions.permissions.clone();
|
||||||
match input_bytes.as_slice() {
|
if let Some(key_with_modifier) = key_with_modifier {
|
||||||
// Y or y
|
match key_with_modifier.bare_key {
|
||||||
&[89] | &[121] => Some(AdjustedInput::PermissionRequestResult(
|
BareKey::Char('y') if key_with_modifier.has_no_modifiers() => {
|
||||||
permissions,
|
Some(AdjustedInput::PermissionRequestResult(
|
||||||
PermissionStatus::Granted,
|
permissions,
|
||||||
)),
|
PermissionStatus::Granted,
|
||||||
// N or n
|
))
|
||||||
&[78] | &[110] => Some(AdjustedInput::PermissionRequestResult(
|
},
|
||||||
permissions,
|
BareKey::Char('n') if key_with_modifier.has_no_modifiers() => {
|
||||||
PermissionStatus::Denied,
|
Some(AdjustedInput::PermissionRequestResult(
|
||||||
)),
|
permissions,
|
||||||
_ => None,
|
PermissionStatus::Denied,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match raw_input_bytes.as_slice() {
|
||||||
|
// Y or y
|
||||||
|
&[89] | &[121] => Some(AdjustedInput::PermissionRequestResult(
|
||||||
|
permissions,
|
||||||
|
PermissionStatus::Granted,
|
||||||
|
)),
|
||||||
|
// N or n
|
||||||
|
&[78] | &[110] => Some(AdjustedInput::PermissionRequestResult(
|
||||||
|
permissions,
|
||||||
|
PermissionStatus::Denied,
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some(key_with_modifier) = key_with_modifier {
|
||||||
|
Some(AdjustedInput::WriteKeyToPlugin(key_with_modifier.clone()))
|
||||||
|
} else if raw_input_bytes.as_slice() == BRACKETED_PASTE_BEGIN
|
||||||
|
|| raw_input_bytes.as_slice() == BRACKETED_PASTE_END
|
||||||
|
{
|
||||||
|
// plugins do not need bracketed paste
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(AdjustedInput::WriteBytesToTerminal(input_bytes))
|
Some(AdjustedInput::WriteBytesToTerminal(raw_input_bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn position_and_size(&self) -> PaneGeom {
|
fn position_and_size(&self) -> PaneGeom {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@ use std::time::{self, Instant};
|
||||||
use zellij_utils::input::command::RunCommand;
|
use zellij_utils::input::command::RunCommand;
|
||||||
use zellij_utils::pane_size::Offset;
|
use zellij_utils::pane_size::Offset;
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
data::{InputMode, Palette, PaletteColor, PaneId as ZellijUtilsPaneId, Style},
|
data::{
|
||||||
|
BareKey, InputMode, KeyWithModifier, Palette, PaletteColor, PaneId as ZellijUtilsPaneId,
|
||||||
|
Style,
|
||||||
|
},
|
||||||
errors::prelude::*,
|
errors::prelude::*,
|
||||||
input::layout::Run,
|
input::layout::Run,
|
||||||
pane_size::PaneGeom,
|
pane_size::PaneGeom,
|
||||||
|
|
@ -37,8 +40,8 @@ const UP_ARROW: &[u8] = &[27, 91, 65];
|
||||||
const DOWN_ARROW: &[u8] = &[27, 91, 66];
|
const DOWN_ARROW: &[u8] = &[27, 91, 66];
|
||||||
const HOME_KEY: &[u8] = &[27, 91, 72];
|
const HOME_KEY: &[u8] = &[27, 91, 72];
|
||||||
const END_KEY: &[u8] = &[27, 91, 70];
|
const END_KEY: &[u8] = &[27, 91, 70];
|
||||||
const BRACKETED_PASTE_BEGIN: &[u8] = &[27, 91, 50, 48, 48, 126];
|
pub const BRACKETED_PASTE_BEGIN: &[u8] = &[27, 91, 50, 48, 48, 126];
|
||||||
const BRACKETED_PASTE_END: &[u8] = &[27, 91, 50, 48, 49, 126];
|
pub const BRACKETED_PASTE_END: &[u8] = &[27, 91, 50, 48, 49, 126];
|
||||||
const ENTER_NEWLINE: &[u8] = &[10];
|
const ENTER_NEWLINE: &[u8] = &[10];
|
||||||
const ESC: &[u8] = &[27];
|
const ESC: &[u8] = &[27];
|
||||||
const ENTER_CARRIAGE_RETURN: &[u8] = &[13];
|
const ENTER_CARRIAGE_RETURN: &[u8] = &[13];
|
||||||
|
|
@ -190,92 +193,71 @@ impl Pane for TerminalPane {
|
||||||
.cursor_coordinates()
|
.cursor_coordinates()
|
||||||
.map(|(x, y)| (x + left, y + top))
|
.map(|(x, y)| (x + left, y + top))
|
||||||
}
|
}
|
||||||
fn adjust_input_to_terminal(&mut self, input_bytes: Vec<u8>) -> Option<AdjustedInput> {
|
fn adjust_input_to_terminal(
|
||||||
|
&mut self,
|
||||||
|
key_with_modifier: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
raw_input_bytes_are_kitty: bool,
|
||||||
|
) -> Option<AdjustedInput> {
|
||||||
// there are some cases in which the terminal state means that input sent to it
|
// there are some cases in which the terminal state means that input sent to it
|
||||||
// needs to be adjusted.
|
// needs to be adjusted.
|
||||||
// here we match against those cases - if need be, we adjust the input and if not
|
// here we match against those cases - if need be, we adjust the input and if not
|
||||||
// we send back the original input
|
// we send back the original input
|
||||||
if let Some((_exit_status, _is_first_run, run_command)) = &self.is_held {
|
|
||||||
match input_bytes.as_slice() {
|
|
||||||
ENTER_CARRIAGE_RETURN | ENTER_NEWLINE | SPACE => {
|
|
||||||
let run_command = run_command.clone();
|
|
||||||
self.is_held = None;
|
|
||||||
self.grid.reset_terminal_state();
|
|
||||||
self.set_should_render(true);
|
|
||||||
self.remove_banner();
|
|
||||||
Some(AdjustedInput::ReRunCommandInThisPane(run_command))
|
|
||||||
},
|
|
||||||
ESC => {
|
|
||||||
// Drop to shell in the same working directory as the command was run
|
|
||||||
let working_dir = run_command.cwd.clone();
|
|
||||||
self.is_held = None;
|
|
||||||
self.grid.reset_terminal_state();
|
|
||||||
self.set_should_render(true);
|
|
||||||
self.remove_banner();
|
|
||||||
Some(AdjustedInput::DropToShellInThisPane { working_dir })
|
|
||||||
},
|
|
||||||
CTRL_C => Some(AdjustedInput::CloseThisPane),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if self.grid.new_line_mode {
|
|
||||||
if let &[13] = input_bytes.as_slice() {
|
|
||||||
// LNM - carriage return is followed by linefeed
|
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
|
||||||
"\u{0d}\u{0a}".as_bytes().to_vec(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if self.grid.cursor_key_mode {
|
|
||||||
match input_bytes.as_slice() {
|
|
||||||
LEFT_ARROW => {
|
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
|
||||||
AnsiEncoding::Left.as_vec_bytes(),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
RIGHT_ARROW => {
|
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
|
||||||
AnsiEncoding::Right.as_vec_bytes(),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
UP_ARROW => {
|
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
|
||||||
AnsiEncoding::Up.as_vec_bytes(),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
DOWN_ARROW => {
|
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
|
||||||
AnsiEncoding::Down.as_vec_bytes(),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
|
|
||||||
HOME_KEY => {
|
if !self.grid.bracketed_paste_mode {
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
// Zellij itself operates in bracketed paste mode, so the terminal sends these
|
||||||
AnsiEncoding::Home.as_vec_bytes(),
|
// instructions (bracketed paste start and bracketed paste end respectively)
|
||||||
));
|
// when pasting input. We only need to make sure not to send them to terminal
|
||||||
},
|
// panes who do not work in this mode
|
||||||
END_KEY => {
|
match raw_input_bytes.as_slice() {
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(
|
BRACKETED_PASTE_BEGIN | BRACKETED_PASTE_END => {
|
||||||
AnsiEncoding::End.as_vec_bytes(),
|
return Some(AdjustedInput::WriteBytesToTerminal(vec![]))
|
||||||
));
|
},
|
||||||
},
|
_ => {},
|
||||||
_ => {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !self.grid.bracketed_paste_mode {
|
if self.is_held.is_some() {
|
||||||
// Zellij itself operates in bracketed paste mode, so the terminal sends these
|
if key_with_modifier
|
||||||
// instructions (bracketed paste start and bracketed paste end respectively)
|
.as_ref()
|
||||||
// when pasting input. We only need to make sure not to send them to terminal
|
.map(|k| k.is_key_without_modifier(BareKey::Enter))
|
||||||
// panes who do not work in this mode
|
.unwrap_or(false)
|
||||||
match input_bytes.as_slice() {
|
{
|
||||||
BRACKETED_PASTE_BEGIN | BRACKETED_PASTE_END => {
|
self.handle_held_run()
|
||||||
return Some(AdjustedInput::WriteBytesToTerminal(vec![]))
|
} else if key_with_modifier
|
||||||
},
|
.as_ref()
|
||||||
_ => {},
|
.map(|k| k.is_key_without_modifier(BareKey::Esc))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
self.handle_held_drop_to_shell()
|
||||||
|
} else if key_with_modifier
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_with_ctrl_modifier(BareKey::Char('c')))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
Some(AdjustedInput::CloseThisPane)
|
||||||
|
} else {
|
||||||
|
match raw_input_bytes.as_slice() {
|
||||||
|
ENTER_CARRIAGE_RETURN | ENTER_NEWLINE | SPACE => self.handle_held_run(),
|
||||||
|
ESC => self.handle_held_drop_to_shell(),
|
||||||
|
CTRL_C => Some(AdjustedInput::CloseThisPane),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(AdjustedInput::WriteBytesToTerminal(input_bytes))
|
} else {
|
||||||
|
if self.grid.supports_kitty_keyboard_protocol {
|
||||||
|
self.adjust_input_to_terminal_with_kitty_keyboard_protocol(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_input_bytes,
|
||||||
|
raw_input_bytes_are_kitty,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.adjust_input_to_terminal_without_kitty_keyboard_protocol(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_input_bytes,
|
||||||
|
raw_input_bytes_are_kitty,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn position_and_size(&self) -> PaneGeom {
|
fn position_and_size(&self) -> PaneGeom {
|
||||||
|
|
@ -804,6 +786,7 @@ impl TerminalPane {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
explicitly_disable_keyboard_protocol: bool,
|
||||||
) -> TerminalPane {
|
) -> TerminalPane {
|
||||||
let initial_pane_title =
|
let initial_pane_title =
|
||||||
initial_pane_title.unwrap_or_else(|| format!("Pane #{}", pane_index));
|
initial_pane_title.unwrap_or_else(|| format!("Pane #{}", pane_index));
|
||||||
|
|
@ -819,6 +802,7 @@ impl TerminalPane {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_keyboard_protocol,
|
||||||
);
|
);
|
||||||
TerminalPane {
|
TerminalPane {
|
||||||
frame: HashMap::new(),
|
frame: HashMap::new(),
|
||||||
|
|
@ -910,6 +894,131 @@ impl TerminalPane {
|
||||||
self.banner = None;
|
self.banner = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn adjust_input_to_terminal_with_kitty_keyboard_protocol(
|
||||||
|
&self,
|
||||||
|
key: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
raw_input_bytes_are_kitty: bool,
|
||||||
|
) -> Option<AdjustedInput> {
|
||||||
|
if raw_input_bytes_are_kitty {
|
||||||
|
Some(AdjustedInput::WriteBytesToTerminal(raw_input_bytes))
|
||||||
|
} else {
|
||||||
|
// here what happens is that the host terminal is operating in non "kitty keys" mode, but
|
||||||
|
// this terminal pane *is* operating in "kitty keys" mode - so we need to serialize the "non kitty"
|
||||||
|
// key to a "kitty key"
|
||||||
|
key.as_ref()
|
||||||
|
.and_then(|k| k.serialize_kitty())
|
||||||
|
.map(|s| AdjustedInput::WriteBytesToTerminal(s.as_bytes().to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn adjust_input_to_terminal_without_kitty_keyboard_protocol(
|
||||||
|
&self,
|
||||||
|
key: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
raw_input_bytes_are_kitty: bool,
|
||||||
|
) -> Option<AdjustedInput> {
|
||||||
|
if self.grid.new_line_mode {
|
||||||
|
let key_is_enter = raw_input_bytes.as_slice() == &[13]
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::Enter))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if key_is_enter {
|
||||||
|
// LNM - carriage return is followed by linefeed
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
"\u{0d}\u{0a}".as_bytes().to_vec(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if self.grid.cursor_key_mode {
|
||||||
|
let key_is_left_arrow = raw_input_bytes.as_slice() == LEFT_ARROW
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::Left))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let key_is_right_arrow = raw_input_bytes.as_slice() == RIGHT_ARROW
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::Right))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let key_is_up_arrow = raw_input_bytes.as_slice() == UP_ARROW
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::Up))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let key_is_down_arrow = raw_input_bytes.as_slice() == DOWN_ARROW
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::Down))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let key_is_home_key = raw_input_bytes.as_slice() == HOME_KEY
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::Home))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let key_is_end_key = raw_input_bytes.as_slice() == END_KEY
|
||||||
|
|| key
|
||||||
|
.as_ref()
|
||||||
|
.map(|k| k.is_key_without_modifier(BareKey::End))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if key_is_left_arrow {
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
AnsiEncoding::Left.as_vec_bytes(),
|
||||||
|
));
|
||||||
|
} else if key_is_right_arrow {
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
AnsiEncoding::Right.as_vec_bytes(),
|
||||||
|
));
|
||||||
|
} else if key_is_up_arrow {
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
AnsiEncoding::Up.as_vec_bytes(),
|
||||||
|
));
|
||||||
|
} else if key_is_down_arrow {
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
AnsiEncoding::Down.as_vec_bytes(),
|
||||||
|
));
|
||||||
|
} else if key_is_home_key {
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
AnsiEncoding::Home.as_vec_bytes(),
|
||||||
|
));
|
||||||
|
} else if key_is_end_key {
|
||||||
|
return Some(AdjustedInput::WriteBytesToTerminal(
|
||||||
|
AnsiEncoding::End.as_vec_bytes(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if raw_input_bytes_are_kitty {
|
||||||
|
// here what happens is that the host terminal is operating in "kitty keys" mode, but
|
||||||
|
// this terminal pane is not - so we need to serialize the kitty key to "non kitty" if
|
||||||
|
// possible - if not possible (eg. with multiple modifiers), we'll return a None here
|
||||||
|
// and write nothing to the terminal pane
|
||||||
|
key.as_ref()
|
||||||
|
.and_then(|k| k.serialize_non_kitty())
|
||||||
|
.map(|s| AdjustedInput::WriteBytesToTerminal(s.as_bytes().to_vec()))
|
||||||
|
} else {
|
||||||
|
Some(AdjustedInput::WriteBytesToTerminal(raw_input_bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn handle_held_run(&mut self) -> Option<AdjustedInput> {
|
||||||
|
self.is_held.take().map(|(_, _, run_command)| {
|
||||||
|
self.is_held = None;
|
||||||
|
self.grid.reset_terminal_state();
|
||||||
|
self.set_should_render(true);
|
||||||
|
self.remove_banner();
|
||||||
|
AdjustedInput::ReRunCommandInThisPane(run_command.clone())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn handle_held_drop_to_shell(&mut self) -> Option<AdjustedInput> {
|
||||||
|
self.is_held.take().map(|(_, _, run_command)| {
|
||||||
|
// Drop to shell in the same working directory as the command was run
|
||||||
|
let working_dir = run_command.cwd.clone();
|
||||||
|
self.is_held = None;
|
||||||
|
self.grid.reset_terminal_state();
|
||||||
|
self.set_should_render(true);
|
||||||
|
self.remove_banner();
|
||||||
|
AdjustedInput::DropToShellInThisPane { working_dir }
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -31,6 +31,7 @@ fn create_pane() -> TerminalPane {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -47,6 +48,7 @@ fn create_pane() -> TerminalPane {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let content = read_fixture();
|
let content = read_fixture();
|
||||||
terminal_pane.handle_pty_bytes(content);
|
terminal_pane.handle_pty_bytes(content);
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ pub fn scrolling_inside_a_pane() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -55,6 +56,7 @@ pub fn scrolling_inside_a_pane() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let mut text_to_fill_pane = String::new();
|
let mut text_to_fill_pane = String::new();
|
||||||
for i in 0..30 {
|
for i in 0..30 {
|
||||||
|
|
@ -87,6 +89,7 @@ pub fn sixel_image_inside_terminal_pane() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -103,6 +106,7 @@ pub fn sixel_image_inside_terminal_pane() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let sixel_image_bytes = "\u{1b}Pq
|
let sixel_image_bytes = "\u{1b}Pq
|
||||||
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0
|
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0
|
||||||
|
|
@ -135,6 +139,7 @@ pub fn partial_sixel_image_inside_terminal_pane() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -151,6 +156,7 @@ pub fn partial_sixel_image_inside_terminal_pane() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let pane_content = read_fixture("sixel-image-500px.six");
|
let pane_content = read_fixture("sixel-image-500px.six");
|
||||||
terminal_pane.handle_pty_bytes(pane_content);
|
terminal_pane.handle_pty_bytes(pane_content);
|
||||||
|
|
@ -177,6 +183,7 @@ pub fn overflowing_sixel_image_inside_terminal_pane() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -193,6 +200,7 @@ pub fn overflowing_sixel_image_inside_terminal_pane() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let pane_content = read_fixture("sixel-image-500px.six");
|
let pane_content = read_fixture("sixel-image-500px.six");
|
||||||
terminal_pane.handle_pty_bytes(pane_content);
|
terminal_pane.handle_pty_bytes(pane_content);
|
||||||
|
|
@ -218,6 +226,7 @@ pub fn scrolling_through_a_sixel_image() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -234,6 +243,7 @@ pub fn scrolling_through_a_sixel_image() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let mut text_to_fill_pane = String::new();
|
let mut text_to_fill_pane = String::new();
|
||||||
for i in 0..30 {
|
for i in 0..30 {
|
||||||
|
|
@ -270,6 +280,7 @@ pub fn multiple_sixel_images_in_pane() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -286,6 +297,7 @@ pub fn multiple_sixel_images_in_pane() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let mut text_to_fill_pane = String::new();
|
let mut text_to_fill_pane = String::new();
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
|
|
@ -320,6 +332,7 @@ pub fn resizing_pane_with_sixel_images() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -336,6 +349,7 @@ pub fn resizing_pane_with_sixel_images() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let mut text_to_fill_pane = String::new();
|
let mut text_to_fill_pane = String::new();
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
|
|
@ -373,6 +387,7 @@ pub fn changing_character_cell_size_with_sixel_images() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -389,6 +404,7 @@ pub fn changing_character_cell_size_with_sixel_images() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
let mut text_to_fill_pane = String::new();
|
let mut text_to_fill_pane = String::new();
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
|
|
@ -431,6 +447,7 @@ pub fn keep_working_after_corrupted_sixel_image() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -447,6 +464,7 @@ pub fn keep_working_after_corrupted_sixel_image() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
|
|
||||||
let sixel_image_bytes = "\u{1b}PI AM CORRUPTED BWAHAHAq
|
let sixel_image_bytes = "\u{1b}PI AM CORRUPTED BWAHAHAq
|
||||||
|
|
@ -487,6 +505,7 @@ pub fn pane_with_frame_position_is_on_frame() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -503,6 +522,7 @@ pub fn pane_with_frame_position_is_on_frame() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
|
|
||||||
terminal_pane.set_content_offset(Offset::frame(1));
|
terminal_pane.set_content_offset(Offset::frame(1));
|
||||||
|
|
@ -579,6 +599,7 @@ pub fn pane_with_bottom_and_right_borders_position_is_on_frame() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -595,6 +616,7 @@ pub fn pane_with_bottom_and_right_borders_position_is_on_frame() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
|
|
||||||
terminal_pane.set_content_offset(Offset::shift(1, 1));
|
terminal_pane.set_content_offset(Offset::shift(1, 1));
|
||||||
|
|
@ -671,6 +693,7 @@ pub fn frameless_pane_position_is_on_frame() {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut terminal_pane = TerminalPane::new(
|
let mut terminal_pane = TerminalPane::new(
|
||||||
pid,
|
pid,
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
|
|
@ -687,6 +710,7 @@ pub fn frameless_pane_position_is_on_frame() {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
); // 0 is the pane index
|
); // 0 is the pane index
|
||||||
|
|
||||||
terminal_pane.set_content_offset(Offset::default());
|
terminal_pane.set_content_offset(Offset::default());
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ use std::collections::BTreeMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use wasmer::Store;
|
use wasmer::Store;
|
||||||
use zellij_utils::data::{Event, Key, PermissionStatus, PermissionType, PluginCapabilities};
|
use zellij_utils::data::{
|
||||||
|
BareKey, Event, KeyWithModifier, PermissionStatus, PermissionType, PluginCapabilities,
|
||||||
|
};
|
||||||
use zellij_utils::errors::ErrorContext;
|
use zellij_utils::errors::ErrorContext;
|
||||||
use zellij_utils::input::layout::{
|
use zellij_utils::input::layout::{
|
||||||
Layout, PluginAlias, PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias,
|
Layout, PluginAlias, PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias,
|
||||||
|
|
@ -1016,8 +1018,8 @@ pub fn switch_to_mode_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('a')), // this triggers a SwitchToMode(Tab) command in the fixture
|
Event::Key(KeyWithModifier::new(BareKey::Char('a'))), // this triggers a SwitchToMode(Tab) command in the fixture
|
||||||
// plugin
|
// plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1088,8 +1090,8 @@ pub fn switch_to_mode_plugin_command_permission_denied() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('a')), // this triggers a SwitchToMode(Tab) command in the fixture
|
Event::Key(KeyWithModifier::new(BareKey::Char('a'))), // this triggers a SwitchToMode(Tab) command in the fixture
|
||||||
// plugin
|
// plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1160,8 +1162,8 @@ pub fn new_tabs_with_layout_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('b')), // this triggers a new_tabs_with_layout command in the fixture
|
Event::Key(KeyWithModifier::new(BareKey::Char('b'))), // this triggers a new_tabs_with_layout command in the fixture
|
||||||
// plugin
|
// plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1246,8 +1248,8 @@ pub fn new_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('c')), // this triggers a new_tab command in the fixture
|
Event::Key(KeyWithModifier::new(BareKey::Char('c'))), // this triggers a new_tab command in the fixture
|
||||||
// plugin
|
// plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1318,7 +1320,7 @@ pub fn go_to_next_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('d')), // this triggers the event in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('d'))), // this triggers the event in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1389,7 +1391,7 @@ pub fn go_to_previous_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('e')), // this triggers the event in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('e'))), // this triggers the event in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1460,7 +1462,7 @@ pub fn resize_focused_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('f')), // this triggers the event in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('f'))), // this triggers the event in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1531,7 +1533,7 @@ pub fn resize_focused_pane_with_direction_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('g')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('g'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1602,7 +1604,7 @@ pub fn focus_next_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('h')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('h'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1673,7 +1675,7 @@ pub fn focus_previous_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('i')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('i'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1744,7 +1746,7 @@ pub fn move_focus_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('j')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('j'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1815,7 +1817,7 @@ pub fn move_focus_or_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('k')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('k'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1886,7 +1888,7 @@ pub fn edit_scrollback_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('m')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('m'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -1957,7 +1959,7 @@ pub fn write_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('n')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('n'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2028,7 +2030,7 @@ pub fn write_chars_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('o')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('o'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2099,7 +2101,7 @@ pub fn toggle_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('p')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('p'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2170,7 +2172,7 @@ pub fn move_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('q')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('q'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2241,7 +2243,7 @@ pub fn move_pane_with_direction_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('r')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('r'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2313,7 +2315,7 @@ pub fn clear_screen_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('s')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('s'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2385,7 +2387,7 @@ pub fn scroll_up_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('t')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('t'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2456,7 +2458,7 @@ pub fn scroll_down_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('u')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('u'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2527,7 +2529,7 @@ pub fn scroll_to_top_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('v')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('v'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2598,7 +2600,7 @@ pub fn scroll_to_bottom_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('w')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('w'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2669,7 +2671,7 @@ pub fn page_scroll_up_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('x')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('x'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2740,7 +2742,7 @@ pub fn page_scroll_down_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('y')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('y'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2811,7 +2813,7 @@ pub fn toggle_focus_fullscreen_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('z')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('z'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2882,7 +2884,7 @@ pub fn toggle_pane_frames_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('1')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('1'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -2953,7 +2955,7 @@ pub fn toggle_pane_embed_or_eject_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('2')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('2'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3024,7 +3026,7 @@ pub fn undo_rename_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('3')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('3'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3095,7 +3097,7 @@ pub fn close_focus_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('4')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('4'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3166,7 +3168,7 @@ pub fn toggle_active_tab_sync_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('5')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('5'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3237,7 +3239,7 @@ pub fn close_focused_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('6')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('6'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3308,7 +3310,7 @@ pub fn undo_rename_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('7')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('7'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3379,7 +3381,7 @@ pub fn previous_swap_layout_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('a')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3450,7 +3452,7 @@ pub fn next_swap_layout_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('b')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3521,7 +3523,7 @@ pub fn go_to_tab_name_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('c')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3592,7 +3594,7 @@ pub fn focus_or_create_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('d')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3663,7 +3665,7 @@ pub fn go_to_tab() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('e')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('e')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3734,7 +3736,7 @@ pub fn start_or_reload_plugin() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('f')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('f')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3812,7 +3814,7 @@ pub fn quit_zellij_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('8')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('8'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
server_thread.join().unwrap(); // this might take a while if the cache is cold
|
server_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3890,7 +3892,7 @@ pub fn detach_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('l')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('l'))), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
server_thread.join().unwrap(); // this might take a while if the cache is cold
|
server_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -3968,7 +3970,7 @@ pub fn open_file_floating_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('h')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('h')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4050,7 +4052,7 @@ pub fn open_file_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('g')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4133,7 +4135,7 @@ pub fn open_file_with_line_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('i')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('i')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4215,7 +4217,7 @@ pub fn open_file_with_line_floating_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('j')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('j')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4297,7 +4299,7 @@ pub fn open_terminal_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('k')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('k')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4375,7 +4377,7 @@ pub fn open_terminal_floating_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('l')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('l')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4453,7 +4455,7 @@ pub fn open_command_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('m')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('m')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4531,7 +4533,7 @@ pub fn open_command_pane_floating_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('n')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('n')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
pty_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4602,7 +4604,7 @@ pub fn switch_to_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('o')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('o')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4673,7 +4675,7 @@ pub fn hide_self_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('p')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('p')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4743,7 +4745,7 @@ pub fn show_self_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('q')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('q')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4814,7 +4816,7 @@ pub fn close_terminal_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('r')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('r')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4885,7 +4887,7 @@ pub fn close_plugin_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('s')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('s')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -4956,7 +4958,7 @@ pub fn focus_terminal_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('t')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('t')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5027,7 +5029,7 @@ pub fn focus_plugin_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('u')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('u')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5098,7 +5100,7 @@ pub fn rename_terminal_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('v')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('v')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5169,7 +5171,7 @@ pub fn rename_plugin_pane_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('w')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('w')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5240,7 +5242,7 @@ pub fn rename_tab_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('x')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('x')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5320,7 +5322,7 @@ pub fn send_configuration_to_plugins() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('z')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('z')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5388,7 +5390,7 @@ pub fn request_plugin_permissions() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('1')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
screen_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5480,7 +5482,7 @@ pub fn granted_permission_request_result() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('1')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap();
|
screen_thread.join().unwrap();
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5571,7 +5573,7 @@ pub fn denied_permission_request_result() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('1')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
screen_thread.join().unwrap();
|
screen_thread.join().unwrap();
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5642,7 +5644,7 @@ pub fn run_command_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('2')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('2')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5720,7 +5722,7 @@ pub fn run_command_with_env_vars_and_cwd_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('3')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('3')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -5798,7 +5800,7 @@ pub fn web_request_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('4')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('4')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -6219,7 +6221,7 @@ pub fn switch_session_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('5')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('5')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -6300,7 +6302,7 @@ pub fn switch_session_with_layout_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('7')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('7')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -6381,7 +6383,7 @@ pub fn switch_session_with_layout_and_cwd_plugin_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('9')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('9')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -6462,7 +6464,7 @@ pub fn disconnect_other_clients_plugins_command() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('6')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('6')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
teardown();
|
teardown();
|
||||||
|
|
@ -6547,13 +6549,13 @@ pub fn run_plugin_in_specific_cwd() {
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Ctrl('8')), // this triggers the enent in the fixture plugin
|
Event::Key(KeyWithModifier::new(BareKey::Char('8')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin
|
||||||
)]));
|
)]));
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
None,
|
None,
|
||||||
Some(client_id),
|
Some(client_id),
|
||||||
Event::Key(Key::Char('8')), // this sends this quit command so tha the test exits cleanly
|
Event::Key(KeyWithModifier::new(BareKey::Char('8'))), // this sends this quit command so tha the test exits cleanly
|
||||||
)]));
|
)]));
|
||||||
teardown();
|
teardown();
|
||||||
server_thread.join().unwrap(); // this might take a while if the cache is cold
|
server_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
---
|
---
|
||||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||||
assertion_line: 1449
|
assertion_line: 2047
|
||||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||||
---
|
---
|
||||||
Some(
|
Some(
|
||||||
WriteCharacter(
|
WriteCharacter(
|
||||||
|
None,
|
||||||
[
|
[
|
||||||
102,
|
102,
|
||||||
111,
|
111,
|
||||||
111,
|
111,
|
||||||
],
|
],
|
||||||
|
false,
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
---
|
---
|
||||||
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||||
assertion_line: 1117
|
assertion_line: 1976
|
||||||
expression: "format!(\"{:#?}\", new_tab_event)"
|
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||||
---
|
---
|
||||||
Some(
|
Some(
|
||||||
WriteCharacter(
|
WriteCharacter(
|
||||||
|
None,
|
||||||
[
|
[
|
||||||
102,
|
102,
|
||||||
111,
|
111,
|
||||||
111,
|
111,
|
||||||
],
|
],
|
||||||
|
false,
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -543,8 +543,6 @@ impl WasmBridge {
|
||||||
mut updates: Vec<(Option<PluginId>, Option<ClientId>, Event)>,
|
mut updates: Vec<(Option<PluginId>, Option<ClientId>, Event)>,
|
||||||
shutdown_sender: Sender<()>,
|
shutdown_sender: Sender<()>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let err_context = || "failed to update plugin state".to_string();
|
|
||||||
|
|
||||||
let plugins_to_update: Vec<(
|
let plugins_to_update: Vec<(
|
||||||
PluginId,
|
PluginId,
|
||||||
ClientId,
|
ClientId,
|
||||||
|
|
|
||||||
|
|
@ -1051,7 +1051,7 @@ fn edit_scrollback(env: &ForeignFunctionEnv) {
|
||||||
|
|
||||||
fn write(env: &ForeignFunctionEnv, bytes: Vec<u8>) {
|
fn write(env: &ForeignFunctionEnv, bytes: Vec<u8>) {
|
||||||
let error_msg = || format!("failed to write in plugin {}", env.plugin_env.name());
|
let error_msg = || format!("failed to write in plugin {}", env.plugin_env.name());
|
||||||
let action = Action::Write(bytes);
|
let action = Action::Write(None, bytes, false);
|
||||||
apply_action!(action, error_msg, env);
|
apply_action!(action, error_msg, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,12 +64,17 @@ pub(crate) fn route_action(
|
||||||
.send_to_screen(ScreenInstruction::ToggleTab(client_id))
|
.send_to_screen(ScreenInstruction::ToggleTab(client_id))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Action::Write(val) => {
|
Action::Write(key_with_modifier, raw_bytes, is_kitty_keyboard_protocol) => {
|
||||||
senders
|
senders
|
||||||
.send_to_screen(ScreenInstruction::ClearScroll(client_id))
|
.send_to_screen(ScreenInstruction::ClearScroll(client_id))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
senders
|
senders
|
||||||
.send_to_screen(ScreenInstruction::WriteCharacter(val, client_id))
|
.send_to_screen(ScreenInstruction::WriteCharacter(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_bytes,
|
||||||
|
is_kitty_keyboard_protocol,
|
||||||
|
client_id,
|
||||||
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Action::WriteChars(val) => {
|
Action::WriteChars(val) => {
|
||||||
|
|
@ -78,7 +83,9 @@ pub(crate) fn route_action(
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
let val = val.into_bytes();
|
let val = val.into_bytes();
|
||||||
senders
|
senders
|
||||||
.send_to_screen(ScreenInstruction::WriteCharacter(val, client_id))
|
.send_to_screen(ScreenInstruction::WriteCharacter(
|
||||||
|
None, val, false, client_id,
|
||||||
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Action::SwitchToMode(mode) => {
|
Action::SwitchToMode(mode) => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use zellij_utils::data::{
|
use zellij_utils::data::{
|
||||||
Direction, PaneManifest, PluginPermission, Resize, ResizeStrategy, SessionInfo,
|
Direction, KeyWithModifier, PaneManifest, PluginPermission, Resize, ResizeStrategy, SessionInfo,
|
||||||
};
|
};
|
||||||
use zellij_utils::errors::prelude::*;
|
use zellij_utils::errors::prelude::*;
|
||||||
use zellij_utils::input::command::RunCommand;
|
use zellij_utils::input::command::RunCommand;
|
||||||
|
|
@ -158,7 +158,8 @@ pub enum ScreenInstruction {
|
||||||
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
|
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
|
||||||
HorizontalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
|
HorizontalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
|
||||||
VerticalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
|
VerticalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
|
||||||
WriteCharacter(Vec<u8>, ClientId),
|
WriteCharacter(Option<KeyWithModifier>, Vec<u8>, bool, ClientId), // bool ->
|
||||||
|
// is_kitty_keyboard_protocol
|
||||||
Resize(ClientId, ResizeStrategy),
|
Resize(ClientId, ResizeStrategy),
|
||||||
SwitchFocus(ClientId),
|
SwitchFocus(ClientId),
|
||||||
FocusNextPane(ClientId),
|
FocusNextPane(ClientId),
|
||||||
|
|
@ -621,6 +622,7 @@ pub(crate) struct Screen {
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
layout_dir: Option<PathBuf>,
|
layout_dir: Option<PathBuf>,
|
||||||
default_layout_name: Option<String>,
|
default_layout_name: Option<String>,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen {
|
impl Screen {
|
||||||
|
|
@ -644,6 +646,7 @@ impl Screen {
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
layout_dir: Option<PathBuf>,
|
layout_dir: Option<PathBuf>,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let session_name = mode_info.session_name.clone().unwrap_or_default();
|
let session_name = mode_info.session_name.clone().unwrap_or_default();
|
||||||
let session_info = SessionInfo::new(session_name.clone());
|
let session_info = SessionInfo::new(session_name.clone());
|
||||||
|
|
@ -684,6 +687,7 @@ impl Screen {
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
resurrectable_sessions,
|
resurrectable_sessions,
|
||||||
layout_dir,
|
layout_dir,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1232,6 +1236,7 @@ impl Screen {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
self.tabs.insert(tab_index, tab);
|
self.tabs.insert(tab_index, tab);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -2317,6 +2322,13 @@ pub(crate) fn screen_thread_main(
|
||||||
config_options.copy_on_select.unwrap_or(true),
|
config_options.copy_on_select.unwrap_or(true),
|
||||||
);
|
);
|
||||||
let styled_underlines = config_options.styled_underlines.unwrap_or(true);
|
let styled_underlines = config_options.styled_underlines.unwrap_or(true);
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = config_options
|
||||||
|
.support_kitty_keyboard_protocol
|
||||||
|
.map(|e| !e) // this is due to the config options wording, if
|
||||||
|
// "support_kitty_keyboard_protocol" is true,
|
||||||
|
// explicitly_disable_kitty_keyboard_protocol is false and vice versa
|
||||||
|
.unwrap_or(false); // by default, we try to support this if the terminal supports it and
|
||||||
|
// the program running inside a pane requests it
|
||||||
|
|
||||||
let thread_senders = bus.senders.clone();
|
let thread_senders = bus.senders.clone();
|
||||||
let mut screen = Screen::new(
|
let mut screen = Screen::new(
|
||||||
|
|
@ -2345,6 +2357,7 @@ pub(crate) fn screen_thread_main(
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
layout_dir,
|
layout_dir,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut pending_tab_ids: HashSet<usize> = HashSet::new();
|
let mut pending_tab_ids: HashSet<usize> = HashSet::new();
|
||||||
|
|
@ -2536,15 +2549,20 @@ pub(crate) fn screen_thread_main(
|
||||||
screen.log_and_report_session_state()?;
|
screen.log_and_report_session_state()?;
|
||||||
screen.render(None)?;
|
screen.render(None)?;
|
||||||
},
|
},
|
||||||
ScreenInstruction::WriteCharacter(bytes, client_id) => {
|
ScreenInstruction::WriteCharacter(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_bytes,
|
||||||
|
is_kitty_keyboard_protocol,
|
||||||
|
client_id,
|
||||||
|
) => {
|
||||||
let mut state_changed = false;
|
let mut state_changed = false;
|
||||||
active_tab_and_connected_client_id!(
|
active_tab_and_connected_client_id!(
|
||||||
screen,
|
screen,
|
||||||
client_id,
|
client_id,
|
||||||
|tab: &mut Tab, client_id: ClientId| {
|
|tab: &mut Tab, client_id: ClientId| {
|
||||||
let write_result = match tab.is_sync_panes_active() {
|
let write_result = match tab.is_sync_panes_active() {
|
||||||
true => tab.write_to_terminals_on_current_tab(bytes, client_id),
|
true => tab.write_to_terminals_on_current_tab(&key_with_modifier, raw_bytes, is_kitty_keyboard_protocol, client_id),
|
||||||
false => tab.write_to_active_terminal(bytes, client_id),
|
false => tab.write_to_active_terminal(&key_with_modifier, raw_bytes, is_kitty_keyboard_protocol, client_id),
|
||||||
};
|
};
|
||||||
if let Ok(true) = write_result {
|
if let Ok(true) = write_result {
|
||||||
state_changed = true;
|
state_changed = true;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ pub struct LayoutApplier<'a> {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LayoutApplier<'a> {
|
impl<'a> LayoutApplier<'a> {
|
||||||
|
|
@ -63,6 +64,7 @@ impl<'a> LayoutApplier<'a> {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let viewport = viewport.clone();
|
let viewport = viewport.clone();
|
||||||
let senders = senders.clone();
|
let senders = senders.clone();
|
||||||
|
|
@ -94,6 +96,7 @@ impl<'a> LayoutApplier<'a> {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn apply_layout(
|
pub fn apply_layout(
|
||||||
|
|
@ -330,6 +333,7 @@ impl<'a> LayoutApplier<'a> {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
|
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
|
||||||
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
||||||
|
|
@ -448,6 +452,7 @@ impl<'a> LayoutApplier<'a> {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
if let Some(pane_initial_contents) = &floating_pane_layout.pane_initial_contents {
|
if let Some(pane_initial_contents) = &floating_pane_layout.pane_initial_contents {
|
||||||
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ use std::env::temp_dir;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use zellij_utils::data::{
|
use zellij_utils::data::{
|
||||||
Direction, PaneInfo, PermissionStatus, PermissionType, PluginPermission, ResizeStrategy,
|
Direction, KeyWithModifier, PaneInfo, PermissionStatus, PermissionType, PluginPermission,
|
||||||
|
ResizeStrategy,
|
||||||
};
|
};
|
||||||
use zellij_utils::errors::prelude::*;
|
use zellij_utils::errors::prelude::*;
|
||||||
use zellij_utils::input::command::RunCommand;
|
use zellij_utils::input::command::RunCommand;
|
||||||
|
|
@ -187,6 +188,7 @@ pub(crate) struct Tab {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
|
@ -215,7 +217,12 @@ pub trait Pane {
|
||||||
fn handle_pty_bytes(&mut self, _bytes: VteBytes) {}
|
fn handle_pty_bytes(&mut self, _bytes: VteBytes) {}
|
||||||
fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) {}
|
fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) {}
|
||||||
fn cursor_coordinates(&self) -> Option<(usize, usize)>;
|
fn cursor_coordinates(&self) -> Option<(usize, usize)>;
|
||||||
fn adjust_input_to_terminal(&mut self, _input_bytes: Vec<u8>) -> Option<AdjustedInput> {
|
fn adjust_input_to_terminal(
|
||||||
|
&mut self,
|
||||||
|
_key_with_modifier: &Option<KeyWithModifier>,
|
||||||
|
_raw_input_bytes: Vec<u8>,
|
||||||
|
_raw_input_bytes_are_kitty: bool,
|
||||||
|
) -> Option<AdjustedInput> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn position_and_size(&self) -> PaneGeom;
|
fn position_and_size(&self) -> PaneGeom;
|
||||||
|
|
@ -487,6 +494,7 @@ pub enum AdjustedInput {
|
||||||
PermissionRequestResult(Vec<PermissionType>, PermissionStatus),
|
PermissionRequestResult(Vec<PermissionType>, PermissionStatus),
|
||||||
CloseThisPane,
|
CloseThisPane,
|
||||||
DropToShellInThisPane { working_dir: Option<PathBuf> },
|
DropToShellInThisPane { working_dir: Option<PathBuf> },
|
||||||
|
WriteKeyToPlugin(KeyWithModifier),
|
||||||
}
|
}
|
||||||
pub fn get_next_terminal_position(
|
pub fn get_next_terminal_position(
|
||||||
tiled_panes: &TiledPanes,
|
tiled_panes: &TiledPanes,
|
||||||
|
|
@ -537,6 +545,7 @@ impl Tab {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
arrow_fonts: bool,
|
arrow_fonts: bool,
|
||||||
styled_underlines: bool,
|
styled_underlines: bool,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let name = if name.is_empty() {
|
let name = if name.is_empty() {
|
||||||
format!("Tab #{}", index + 1)
|
format!("Tab #{}", index + 1)
|
||||||
|
|
@ -627,6 +636,7 @@ impl Tab {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -660,6 +670,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
)
|
)
|
||||||
.apply_layout(
|
.apply_layout(
|
||||||
layout,
|
layout,
|
||||||
|
|
@ -723,6 +734,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
)
|
)
|
||||||
.apply_floating_panes_layout_to_existing_panes(
|
.apply_floating_panes_layout_to_existing_panes(
|
||||||
&layout_candidate,
|
&layout_candidate,
|
||||||
|
|
@ -779,6 +791,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
)
|
)
|
||||||
.apply_tiled_panes_layout_to_existing_panes(
|
.apply_tiled_panes_layout_to_existing_panes(
|
||||||
&layout_candidate,
|
&layout_candidate,
|
||||||
|
|
@ -1084,6 +1097,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
)) as Box<dyn Pane>
|
)) as Box<dyn Pane>
|
||||||
},
|
},
|
||||||
PaneId::Plugin(plugin_pid) => {
|
PaneId::Plugin(plugin_pid) => {
|
||||||
|
|
@ -1146,6 +1160,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
new_pane.update_name("EDITING SCROLLBACK"); // we do this here and not in the
|
new_pane.update_name("EDITING SCROLLBACK"); // we do this here and not in the
|
||||||
// constructor so it won't be overrided
|
// constructor so it won't be overrided
|
||||||
|
|
@ -1220,6 +1235,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let replaced_pane = if self.floating_panes.panes_contain(&old_pane_id) {
|
let replaced_pane = if self.floating_panes.panes_contain(&old_pane_id) {
|
||||||
self.floating_panes
|
self.floating_panes
|
||||||
|
|
@ -1344,6 +1360,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
self.tiled_panes
|
self.tiled_panes
|
||||||
.split_pane_horizontally(pid, Box::new(new_terminal), client_id);
|
.split_pane_horizontally(pid, Box::new(new_terminal), client_id);
|
||||||
|
|
@ -1403,6 +1420,7 @@ impl Tab {
|
||||||
self.debug,
|
self.debug,
|
||||||
self.arrow_fonts,
|
self.arrow_fonts,
|
||||||
self.styled_underlines,
|
self.styled_underlines,
|
||||||
|
self.explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
self.tiled_panes
|
self.tiled_panes
|
||||||
.split_pane_vertically(pid, Box::new(new_terminal), client_id);
|
.split_pane_vertically(pid, Box::new(new_terminal), client_id);
|
||||||
|
|
@ -1591,7 +1609,7 @@ impl Tab {
|
||||||
let messages_to_pty = terminal_output.drain_messages_to_pty();
|
let messages_to_pty = terminal_output.drain_messages_to_pty();
|
||||||
let clipboard_update = terminal_output.drain_clipboard_update();
|
let clipboard_update = terminal_output.drain_clipboard_update();
|
||||||
for message in messages_to_pty {
|
for message in messages_to_pty {
|
||||||
self.write_to_pane_id(message, PaneId::Terminal(pid), None)
|
self.write_to_pane_id_without_preprocessing(message, PaneId::Terminal(pid))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
if let Some(string) = clipboard_update {
|
if let Some(string) = clipboard_update {
|
||||||
|
|
@ -1604,7 +1622,9 @@ impl Tab {
|
||||||
|
|
||||||
pub fn write_to_terminals_on_current_tab(
|
pub fn write_to_terminals_on_current_tab(
|
||||||
&mut self,
|
&mut self,
|
||||||
input_bytes: Vec<u8>,
|
key_with_modifier: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
raw_input_bytes_are_kitty: bool,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
// returns true if a UI update should be triggered (eg. when closing a command pane with
|
// returns true if a UI update should be triggered (eg. when closing a command pane with
|
||||||
|
|
@ -1613,7 +1633,13 @@ impl Tab {
|
||||||
let pane_ids = self.get_static_and_floating_pane_ids();
|
let pane_ids = self.get_static_and_floating_pane_ids();
|
||||||
for pane_id in pane_ids {
|
for pane_id in pane_ids {
|
||||||
let ui_change_triggered = self
|
let ui_change_triggered = self
|
||||||
.write_to_pane_id(input_bytes.clone(), pane_id, Some(client_id))
|
.write_to_pane_id(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_input_bytes.clone(),
|
||||||
|
raw_input_bytes_are_kitty,
|
||||||
|
pane_id,
|
||||||
|
Some(client_id),
|
||||||
|
)
|
||||||
.context("failed to write to terminals on current tab")?;
|
.context("failed to write to terminals on current tab")?;
|
||||||
if ui_change_triggered {
|
if ui_change_triggered {
|
||||||
should_trigger_ui_change = true;
|
should_trigger_ui_change = true;
|
||||||
|
|
@ -1624,14 +1650,16 @@ impl Tab {
|
||||||
|
|
||||||
pub fn write_to_active_terminal(
|
pub fn write_to_active_terminal(
|
||||||
&mut self,
|
&mut self,
|
||||||
input_bytes: Vec<u8>,
|
key_with_modifier: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
raw_input_bytes_are_kitty: bool,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
// returns true if a UI update should be triggered (eg. if a command pane
|
// returns true if a UI update should be triggered (eg. if a command pane
|
||||||
// was closed with ctrl-c)
|
// was closed with ctrl-c)
|
||||||
let err_context = || {
|
let err_context = || {
|
||||||
format!(
|
format!(
|
||||||
"failed to write to active terminal for client {client_id} - msg: {input_bytes:?}"
|
"failed to write to active terminal for client {client_id} - msg: {raw_input_bytes:?}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1651,9 +1679,15 @@ impl Tab {
|
||||||
.get_active_pane_id(client_id)
|
.get_active_pane_id(client_id)
|
||||||
.with_context(err_context)?
|
.with_context(err_context)?
|
||||||
};
|
};
|
||||||
// Can't use 'err_context' here since it borrows 'input_bytes'
|
// Can't use 'err_context' here since it borrows 'raw_input_bytes'
|
||||||
self.write_to_pane_id(input_bytes, pane_id, Some(client_id))
|
self.write_to_pane_id(
|
||||||
.with_context(|| format!("failed to write to active terminal for client {client_id}"))
|
key_with_modifier,
|
||||||
|
raw_input_bytes,
|
||||||
|
raw_input_bytes_are_kitty,
|
||||||
|
pane_id,
|
||||||
|
Some(client_id),
|
||||||
|
)
|
||||||
|
.with_context(|| format!("failed to write to active terminal for client {client_id}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_terminal_at(
|
pub fn write_to_terminal_at(
|
||||||
|
|
@ -1670,7 +1704,7 @@ impl Tab {
|
||||||
.get_pane_id_at(position, false)
|
.get_pane_id_at(position, false)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
if let Some(pane_id) = pane_id {
|
if let Some(pane_id) = pane_id {
|
||||||
self.write_to_pane_id(input_bytes, pane_id, Some(client_id))
|
self.write_to_pane_id(&None, input_bytes, false, pane_id, Some(client_id))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -1680,7 +1714,7 @@ impl Tab {
|
||||||
.get_pane_id_at(position, false)
|
.get_pane_id_at(position, false)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
if let Some(pane_id) = pane_id {
|
if let Some(pane_id) = pane_id {
|
||||||
self.write_to_pane_id(input_bytes, pane_id, Some(client_id))
|
self.write_to_pane_id(&None, input_bytes, false, pane_id, Some(client_id))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -1689,7 +1723,9 @@ impl Tab {
|
||||||
|
|
||||||
pub fn write_to_pane_id(
|
pub fn write_to_pane_id(
|
||||||
&mut self,
|
&mut self,
|
||||||
input_bytes: Vec<u8>,
|
key_with_modifier: &Option<KeyWithModifier>,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
raw_input_bytes_are_kitty: bool,
|
||||||
pane_id: PaneId,
|
pane_id: PaneId,
|
||||||
client_id: Option<ClientId>,
|
client_id: Option<ClientId>,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
|
|
@ -1720,7 +1756,11 @@ impl Tab {
|
||||||
|
|
||||||
match pane_id {
|
match pane_id {
|
||||||
PaneId::Terminal(active_terminal_id) => {
|
PaneId::Terminal(active_terminal_id) => {
|
||||||
match active_terminal.adjust_input_to_terminal(input_bytes) {
|
match active_terminal.adjust_input_to_terminal(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_input_bytes,
|
||||||
|
raw_input_bytes_are_kitty,
|
||||||
|
) {
|
||||||
Some(AdjustedInput::WriteBytesToTerminal(adjusted_input)) => {
|
Some(AdjustedInput::WriteBytesToTerminal(adjusted_input)) => {
|
||||||
self.senders
|
self.senders
|
||||||
.send_to_pty_writer(PtyWriteInstruction::Write(
|
.send_to_pty_writer(PtyWriteInstruction::Write(
|
||||||
|
|
@ -1758,12 +1798,26 @@ impl Tab {
|
||||||
None => {},
|
None => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PaneId::Plugin(pid) => match active_terminal.adjust_input_to_terminal(input_bytes) {
|
PaneId::Plugin(pid) => match active_terminal.adjust_input_to_terminal(
|
||||||
|
key_with_modifier,
|
||||||
|
raw_input_bytes,
|
||||||
|
raw_input_bytes_are_kitty,
|
||||||
|
) {
|
||||||
|
Some(AdjustedInput::WriteKeyToPlugin(key_with_modifier)) => {
|
||||||
|
self.senders
|
||||||
|
.send_to_plugin(PluginInstruction::Update(vec![(
|
||||||
|
Some(pid),
|
||||||
|
client_id,
|
||||||
|
Event::Key(key_with_modifier),
|
||||||
|
)]))
|
||||||
|
.with_context(err_context)?;
|
||||||
|
},
|
||||||
Some(AdjustedInput::WriteBytesToTerminal(adjusted_input)) => {
|
Some(AdjustedInput::WriteBytesToTerminal(adjusted_input)) => {
|
||||||
let mut plugin_updates = vec![];
|
let mut plugin_updates = vec![];
|
||||||
for key in parse_keys(&adjusted_input) {
|
for key in parse_keys(&adjusted_input) {
|
||||||
plugin_updates.push((Some(pid), client_id, Event::Key(key)));
|
plugin_updates.push((Some(pid), client_id, Event::Key(key)));
|
||||||
}
|
}
|
||||||
|
log::info!("plugin_updates: {:?}", plugin_updates);
|
||||||
self.senders
|
self.senders
|
||||||
.send_to_plugin(PluginInstruction::Update(plugin_updates))
|
.send_to_plugin(PluginInstruction::Update(plugin_updates))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
|
|
@ -1787,6 +1841,32 @@ impl Tab {
|
||||||
}
|
}
|
||||||
Ok(should_update_ui)
|
Ok(should_update_ui)
|
||||||
}
|
}
|
||||||
|
pub fn write_to_pane_id_without_preprocessing(
|
||||||
|
&mut self,
|
||||||
|
raw_input_bytes: Vec<u8>,
|
||||||
|
pane_id: PaneId,
|
||||||
|
) -> Result<bool> {
|
||||||
|
// returns true if we need to update the UI (eg. when a command pane is closed with ctrl-c)
|
||||||
|
let err_context = || format!("failed to write to pane with id {pane_id:?}");
|
||||||
|
|
||||||
|
let mut should_update_ui = false;
|
||||||
|
|
||||||
|
match pane_id {
|
||||||
|
PaneId::Terminal(active_terminal_id) => {
|
||||||
|
self.senders
|
||||||
|
.send_to_pty_writer(PtyWriteInstruction::Write(
|
||||||
|
raw_input_bytes,
|
||||||
|
active_terminal_id,
|
||||||
|
))
|
||||||
|
.with_context(err_context)?;
|
||||||
|
should_update_ui = true;
|
||||||
|
},
|
||||||
|
PaneId::Plugin(_pid) => {
|
||||||
|
log::error!("Unsupported plugin action");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(should_update_ui)
|
||||||
|
}
|
||||||
pub fn get_active_terminal_cursor_position(
|
pub fn get_active_terminal_cursor_position(
|
||||||
&self,
|
&self,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
|
|
@ -2889,8 +2969,13 @@ impl Tab {
|
||||||
let relative_position = pane.relative_position(position);
|
let relative_position = pane.relative_position(position);
|
||||||
if let Some(mouse_event) = pane.mouse_left_click(&relative_position, false) {
|
if let Some(mouse_event) = pane.mouse_left_click(&relative_position, false) {
|
||||||
if !pane.position_is_on_frame(position) {
|
if !pane.position_is_on_frame(position) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(
|
||||||
.with_context(err_context)?;
|
&None,
|
||||||
|
mouse_event.into_bytes(),
|
||||||
|
false,
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pane.start_selection(&relative_position, client_id);
|
pane.start_selection(&relative_position, client_id);
|
||||||
|
|
@ -2919,8 +3004,13 @@ impl Tab {
|
||||||
let relative_position = pane.relative_position(position);
|
let relative_position = pane.relative_position(position);
|
||||||
if let Some(mouse_event) = pane.mouse_right_click(&relative_position, false) {
|
if let Some(mouse_event) = pane.mouse_right_click(&relative_position, false) {
|
||||||
if !pane.position_is_on_frame(position) {
|
if !pane.position_is_on_frame(position) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(
|
||||||
.with_context(err_context)?;
|
&None,
|
||||||
|
mouse_event.into_bytes(),
|
||||||
|
false,
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pane.handle_right_click(&relative_position, client_id);
|
pane.handle_right_click(&relative_position, client_id);
|
||||||
|
|
@ -2946,8 +3036,13 @@ impl Tab {
|
||||||
let relative_position = pane.relative_position(position);
|
let relative_position = pane.relative_position(position);
|
||||||
if let Some(mouse_event) = pane.mouse_middle_click(&relative_position, false) {
|
if let Some(mouse_event) = pane.mouse_middle_click(&relative_position, false) {
|
||||||
if !pane.position_is_on_frame(position) {
|
if !pane.position_is_on_frame(position) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(
|
||||||
.with_context(err_context)?;
|
&None,
|
||||||
|
mouse_event.into_bytes(),
|
||||||
|
false,
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -3006,7 +3101,7 @@ impl Tab {
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(mouse_event) = active_pane.mouse_right_click_release(&relative_position) {
|
if let Some(mouse_event) = active_pane.mouse_right_click_release(&relative_position) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(&None, mouse_event.into_bytes(), false, client_id)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3039,7 +3134,7 @@ impl Tab {
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(mouse_event) = active_pane.mouse_middle_click_release(&relative_position) {
|
if let Some(mouse_event) = active_pane.mouse_middle_click_release(&relative_position) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(&None, mouse_event.into_bytes(), false, client_id)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3084,7 +3179,7 @@ impl Tab {
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(mouse_event) = active_pane.mouse_left_click_release(&relative_position) {
|
if let Some(mouse_event) = active_pane.mouse_left_click_release(&relative_position) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(&None, mouse_event.into_bytes(), false, client_id)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
} else {
|
} else {
|
||||||
let relative_position = active_pane.relative_position(position);
|
let relative_position = active_pane.relative_position(position);
|
||||||
|
|
@ -3163,8 +3258,13 @@ impl Tab {
|
||||||
.min(active_pane.get_content_rows() as isize),
|
.min(active_pane.get_content_rows() as isize),
|
||||||
);
|
);
|
||||||
if let Some(mouse_event) = active_pane.mouse_left_click(&relative_position, true) {
|
if let Some(mouse_event) = active_pane.mouse_left_click(&relative_position, true) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(
|
||||||
.with_context(err_context)?;
|
&None,
|
||||||
|
mouse_event.into_bytes(),
|
||||||
|
false,
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.with_context(err_context)?;
|
||||||
return Ok(true); // we need to re-render in this case so the selection disappears
|
return Ok(true); // we need to re-render in this case so the selection disappears
|
||||||
}
|
}
|
||||||
} else if selecting {
|
} else if selecting {
|
||||||
|
|
@ -3210,8 +3310,13 @@ impl Tab {
|
||||||
.min(active_pane.get_content_rows() as isize),
|
.min(active_pane.get_content_rows() as isize),
|
||||||
);
|
);
|
||||||
if let Some(mouse_event) = active_pane.mouse_right_click(&relative_position, true) {
|
if let Some(mouse_event) = active_pane.mouse_right_click(&relative_position, true) {
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(
|
||||||
.with_context(err_context)?;
|
&None,
|
||||||
|
mouse_event.into_bytes(),
|
||||||
|
false,
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.with_context(err_context)?;
|
||||||
return Ok(true); // we need to re-render in this case so the selection disappears
|
return Ok(true); // we need to re-render in this case so the selection disappears
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3254,8 +3359,13 @@ impl Tab {
|
||||||
);
|
);
|
||||||
if let Some(mouse_event) = active_pane.mouse_middle_click(&relative_position, true)
|
if let Some(mouse_event) = active_pane.mouse_middle_click(&relative_position, true)
|
||||||
{
|
{
|
||||||
self.write_to_active_terminal(mouse_event.into_bytes(), client_id)
|
self.write_to_active_terminal(
|
||||||
.with_context(err_context)?;
|
&None,
|
||||||
|
mouse_event.into_bytes(),
|
||||||
|
false,
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.with_context(err_context)?;
|
||||||
return Ok(true); // we need to re-render in this case so the selection disappears
|
return Ok(true); // we need to re-render in this case so the selection disappears
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -251,6 +252,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
TiledPaneLayout::default(),
|
TiledPaneLayout::default(),
|
||||||
|
|
@ -303,6 +305,7 @@ fn create_new_tab_with_swap_layouts(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -328,6 +331,7 @@ fn create_new_tab_with_swap_layouts(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let (
|
let (
|
||||||
base_layout,
|
base_layout,
|
||||||
|
|
@ -382,6 +386,7 @@ fn create_new_tab_with_os_api(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -407,6 +412,7 @@ fn create_new_tab_with_os_api(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
TiledPaneLayout::default(),
|
TiledPaneLayout::default(),
|
||||||
|
|
@ -447,6 +453,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -472,6 +479,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let pane_ids = tab_layout
|
let pane_ids = tab_layout
|
||||||
.extract_run_instructions()
|
.extract_run_instructions()
|
||||||
|
|
@ -526,6 +534,7 @@ fn create_new_tab_with_mock_pty_writer(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -551,6 +560,7 @@ fn create_new_tab_with_mock_pty_writer(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
TiledPaneLayout::default(),
|
TiledPaneLayout::default(),
|
||||||
|
|
@ -596,6 +606,7 @@ fn create_new_tab_with_sixel_support(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -621,6 +632,7 @@ fn create_new_tab_with_sixel_support(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
TiledPaneLayout::default(),
|
TiledPaneLayout::default(),
|
||||||
|
|
@ -659,6 +671,7 @@ fn take_snapshot(ansi_instructions: &str, rows: usize, columns: usize, palette:
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut grid = Grid::new(
|
let mut grid = Grid::new(
|
||||||
rows,
|
rows,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -671,6 +684,7 @@ fn take_snapshot(ansi_instructions: &str, rows: usize, columns: usize, palette:
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let mut vte_parser = vte::Parser::new();
|
let mut vte_parser = vte::Parser::new();
|
||||||
for &byte in ansi_instructions.as_bytes() {
|
for &byte in ansi_instructions.as_bytes() {
|
||||||
|
|
@ -694,6 +708,7 @@ fn take_snapshot_with_sixel(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut grid = Grid::new(
|
let mut grid = Grid::new(
|
||||||
rows,
|
rows,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -706,6 +721,7 @@ fn take_snapshot_with_sixel(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let mut vte_parser = vte::Parser::new();
|
let mut vte_parser = vte::Parser::new();
|
||||||
for &byte in ansi_instructions.as_bytes() {
|
for &byte in ansi_instructions.as_bytes() {
|
||||||
|
|
@ -726,6 +742,7 @@ fn take_snapshot_and_cursor_position(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut grid = Grid::new(
|
let mut grid = Grid::new(
|
||||||
rows,
|
rows,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -738,6 +755,7 @@ fn take_snapshot_and_cursor_position(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let mut vte_parser = vte::Parser::new();
|
let mut vte_parser = vte::Parser::new();
|
||||||
for &byte in ansi_instructions.as_bytes() {
|
for &byte in ansi_instructions.as_bytes() {
|
||||||
|
|
@ -2882,11 +2900,11 @@ fn pane_bracketed_paste_ignored_when_not_in_bracketed_paste_mode() {
|
||||||
|
|
||||||
let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~
|
let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~
|
||||||
let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201
|
let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201
|
||||||
tab.write_to_active_terminal(bracketed_paste_start, client_id)
|
tab.write_to_active_terminal(&None, bracketed_paste_start, false, client_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tab.write_to_active_terminal("test".as_bytes().to_vec(), client_id)
|
tab.write_to_active_terminal(&None, "test".as_bytes().to_vec(), false, client_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tab.write_to_active_terminal(bracketed_paste_end, client_id)
|
tab.write_to_active_terminal(&None, bracketed_paste_end, false, client_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
pty_instruction_bus.exit();
|
pty_instruction_bus.exit();
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ fn create_new_tab(size: Size) -> Tab {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -192,6 +193,7 @@ fn create_new_tab(size: Size) -> Tab {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
TiledPaneLayout::default(),
|
TiledPaneLayout::default(),
|
||||||
|
|
@ -229,6 +231,7 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -254,6 +257,7 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab {
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let mut new_terminal_ids = vec![];
|
let mut new_terminal_ids = vec![];
|
||||||
for i in 0..layout.extract_run_instructions().len() {
|
for i in 0..layout.extract_run_instructions().len() {
|
||||||
|
|
@ -297,6 +301,7 @@ fn create_new_tab_with_cell_size(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -322,6 +327,7 @@ fn create_new_tab_with_cell_size(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
TiledPaneLayout::default(),
|
TiledPaneLayout::default(),
|
||||||
|
|
@ -352,8 +358,14 @@ fn write_to_suppressed_pane() {
|
||||||
// Make sure it's suppressed now
|
// Make sure it's suppressed now
|
||||||
tab.suppressed_panes.get(&PaneId::Terminal(2)).unwrap();
|
tab.suppressed_panes.get(&PaneId::Terminal(2)).unwrap();
|
||||||
// Write content to it
|
// Write content to it
|
||||||
tab.write_to_pane_id(vec![34, 127, 31, 82, 17, 182], PaneId::Terminal(2), None)
|
tab.write_to_pane_id(
|
||||||
.unwrap();
|
&None,
|
||||||
|
vec![34, 127, 31, 82, 17, 182],
|
||||||
|
false,
|
||||||
|
PaneId::Terminal(2),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ fn take_snapshots_and_cursor_coordinates_from_render_events<'a>(
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let mut grid = Grid::new(
|
let mut grid = Grid::new(
|
||||||
screen_size.rows,
|
screen_size.rows,
|
||||||
screen_size.cols,
|
screen_size.cols,
|
||||||
|
|
@ -82,6 +83,7 @@ fn take_snapshots_and_cursor_coordinates_from_render_events<'a>(
|
||||||
debug,
|
debug,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
let snapshots: Vec<(Option<(usize, usize)>, String)> = all_events
|
let snapshots: Vec<(Option<(usize, usize)>, String)> = all_events
|
||||||
.filter_map(|server_instruction| {
|
.filter_map(|server_instruction| {
|
||||||
|
|
@ -252,6 +254,7 @@ fn create_new_screen(size: Size) -> Screen {
|
||||||
let debug = false;
|
let debug = false;
|
||||||
let styled_underlines = true;
|
let styled_underlines = true;
|
||||||
let arrow_fonts = true;
|
let arrow_fonts = true;
|
||||||
|
let explicitly_disable_kitty_keyboard_protocol = false;
|
||||||
let screen = Screen::new(
|
let screen = Screen::new(
|
||||||
bus,
|
bus,
|
||||||
&client_attributes,
|
&client_attributes,
|
||||||
|
|
@ -271,6 +274,7 @@ fn create_new_screen(size: Size) -> Screen {
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
arrow_fonts,
|
arrow_fonts,
|
||||||
layout_dir,
|
layout_dir,
|
||||||
|
explicitly_disable_kitty_keyboard_protocol,
|
||||||
);
|
);
|
||||||
screen
|
screen
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ thiserror = "1.0.30"
|
||||||
unicode-width = "0.1.8"
|
unicode-width = "0.1.8"
|
||||||
url = { version = "2.2.2", features = ["serde"] }
|
url = { version = "2.2.2", features = ["serde"] }
|
||||||
uuid = { version = "1.4.1", features = ["serde", "v4"] }
|
uuid = { version = "1.4.1", features = ["serde", "v4"] }
|
||||||
|
bitflags = "2.5.0"
|
||||||
vte = { version = "0.11.0", default-features = false }
|
vte = { version = "0.11.0", default-features = false }
|
||||||
|
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
|
|
||||||
|
|
@ -363,3 +363,8 @@ plugins {
|
||||||
// Default: false
|
// Default: false
|
||||||
//
|
//
|
||||||
// disable_session_metadata true
|
// disable_session_metadata true
|
||||||
|
|
||||||
|
// Enable or disable support for the enhanced Kitty Keyboard Protocol (the host terminal must also support it)
|
||||||
|
// Default: true (if the host terminal supports it)
|
||||||
|
//
|
||||||
|
// support_kitty_keyboard_protocol false
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -3,6 +3,8 @@
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
#[prost(enumeration = "key::KeyModifier", optional, tag = "1")]
|
#[prost(enumeration = "key::KeyModifier", optional, tag = "1")]
|
||||||
pub modifier: ::core::option::Option<i32>,
|
pub modifier: ::core::option::Option<i32>,
|
||||||
|
#[prost(enumeration = "key::KeyModifier", repeated, tag = "4")]
|
||||||
|
pub additional_modifiers: ::prost::alloc::vec::Vec<i32>,
|
||||||
#[prost(oneof = "key::MainKey", tags = "2, 3")]
|
#[prost(oneof = "key::MainKey", tags = "2, 3")]
|
||||||
pub main_key: ::core::option::Option<key::MainKey>,
|
pub main_key: ::core::option::Option<key::MainKey>,
|
||||||
}
|
}
|
||||||
|
|
@ -23,6 +25,8 @@ pub mod key {
|
||||||
pub enum KeyModifier {
|
pub enum KeyModifier {
|
||||||
Ctrl = 0,
|
Ctrl = 0,
|
||||||
Alt = 1,
|
Alt = 1,
|
||||||
|
Shift = 2,
|
||||||
|
Super = 3,
|
||||||
}
|
}
|
||||||
impl KeyModifier {
|
impl KeyModifier {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
|
@ -33,6 +37,8 @@ pub mod key {
|
||||||
match self {
|
match self {
|
||||||
KeyModifier::Ctrl => "CTRL",
|
KeyModifier::Ctrl => "CTRL",
|
||||||
KeyModifier::Alt => "ALT",
|
KeyModifier::Alt => "ALT",
|
||||||
|
KeyModifier::Shift => "SHIFT",
|
||||||
|
KeyModifier::Super => "SUPER",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|
@ -40,6 +46,8 @@ pub mod key {
|
||||||
match value {
|
match value {
|
||||||
"CTRL" => Some(Self::Ctrl),
|
"CTRL" => Some(Self::Ctrl),
|
||||||
"ALT" => Some(Self::Alt),
|
"ALT" => Some(Self::Alt),
|
||||||
|
"SHIFT" => Some(Self::Shift),
|
||||||
|
"SUPER" => Some(Self::Super),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +90,13 @@ pub mod key {
|
||||||
F12 = 22,
|
F12 = 22,
|
||||||
Tab = 23,
|
Tab = 23,
|
||||||
Esc = 24,
|
Esc = 24,
|
||||||
|
CapsLock = 25,
|
||||||
|
ScrollLock = 26,
|
||||||
|
NumLock = 27,
|
||||||
|
PrintScreen = 28,
|
||||||
|
Pause = 29,
|
||||||
|
Menu = 30,
|
||||||
|
Enter = 31,
|
||||||
}
|
}
|
||||||
impl NamedKey {
|
impl NamedKey {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
|
@ -115,6 +130,13 @@ pub mod key {
|
||||||
NamedKey::F12 => "F12",
|
NamedKey::F12 => "F12",
|
||||||
NamedKey::Tab => "Tab",
|
NamedKey::Tab => "Tab",
|
||||||
NamedKey::Esc => "Esc",
|
NamedKey::Esc => "Esc",
|
||||||
|
NamedKey::CapsLock => "CapsLock",
|
||||||
|
NamedKey::ScrollLock => "ScrollLock",
|
||||||
|
NamedKey::NumLock => "NumLock",
|
||||||
|
NamedKey::PrintScreen => "PrintScreen",
|
||||||
|
NamedKey::Pause => "Pause",
|
||||||
|
NamedKey::Menu => "Menu",
|
||||||
|
NamedKey::Enter => "Enter",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|
@ -145,6 +167,13 @@ pub mod key {
|
||||||
"F12" => Some(Self::F12),
|
"F12" => Some(Self::F12),
|
||||||
"Tab" => Some(Self::Tab),
|
"Tab" => Some(Self::Tab),
|
||||||
"Esc" => Some(Self::Esc),
|
"Esc" => Some(Self::Esc),
|
||||||
|
"CapsLock" => Some(Self::CapsLock),
|
||||||
|
"ScrollLock" => Some(Self::ScrollLock),
|
||||||
|
"NumLock" => Some(Self::NumLock),
|
||||||
|
"PrintScreen" => Some(Self::PrintScreen),
|
||||||
|
"Pause" => Some(Self::Pause),
|
||||||
|
"Menu" => Some(Self::Menu),
|
||||||
|
"Enter" => Some(Self::Enter),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,20 @@ use crate::input::config::ConversionError;
|
||||||
use crate::input::layout::SplitSize;
|
use crate::input::layout::SplitSize;
|
||||||
use clap::ArgEnum;
|
use clap::ArgEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::Metadata;
|
use std::fs::Metadata;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::{self, FromStr};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString, ToString};
|
use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString, ToString};
|
||||||
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
use termwiz::{
|
||||||
|
escape::csi::KittyKeyboardFlags,
|
||||||
|
input::{KeyCode, KeyCodeEncodeModes, KeyboardEncoding, Modifiers},
|
||||||
|
};
|
||||||
|
|
||||||
pub type ClientId = u16; // TODO: merge with crate type?
|
pub type ClientId = u16; // TODO: merge with crate type?
|
||||||
|
|
||||||
pub fn client_id_to_colors(
|
pub fn client_id_to_colors(
|
||||||
|
|
@ -37,13 +43,107 @@ pub fn single_client_color(colors: Palette) -> (PaletteColor, PaletteColor) {
|
||||||
(colors.green, colors.black)
|
(colors.green, colors.black)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add a shortened string representation (beyond `Display::fmt` below) that can be used when
|
impl FromStr for KeyWithModifier {
|
||||||
// screen space is scarce. Useful for e.g. "ENTER", "SPACE", "TAB" to display as Unicode
|
type Err = Box<dyn std::error::Error>;
|
||||||
// representations instead.
|
fn from_str(key_str: &str) -> Result<Self, Self::Err> {
|
||||||
// NOTE: Do not reorder the key variants since that influences what the `status_bar` plugin
|
let mut key_string_parts: Vec<&str> = key_str.split_ascii_whitespace().collect();
|
||||||
// displays!
|
let bare_key: BareKey = BareKey::from_str(key_string_parts.pop().ok_or("empty key")?)?;
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
|
let mut key_modifiers: BTreeSet<KeyModifier> = BTreeSet::new();
|
||||||
pub enum Key {
|
for stringified_modifier in key_string_parts {
|
||||||
|
key_modifiers.insert(KeyModifier::from_str(stringified_modifier)?);
|
||||||
|
}
|
||||||
|
Ok(KeyWithModifier {
|
||||||
|
bare_key,
|
||||||
|
key_modifiers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
|
pub struct KeyWithModifier {
|
||||||
|
pub bare_key: BareKey,
|
||||||
|
pub key_modifiers: BTreeSet<KeyModifier>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for KeyWithModifier {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self.bare_key, other.bare_key) {
|
||||||
|
(BareKey::Char(self_char), BareKey::Char(other_char))
|
||||||
|
if self_char.to_ascii_lowercase() == other_char.to_ascii_lowercase() =>
|
||||||
|
{
|
||||||
|
let mut self_cloned = self.clone();
|
||||||
|
let mut other_cloned = other.clone();
|
||||||
|
if self_char.is_ascii_uppercase() {
|
||||||
|
self_cloned.bare_key = BareKey::Char(self_char.to_ascii_lowercase());
|
||||||
|
self_cloned.key_modifiers.insert(KeyModifier::Shift);
|
||||||
|
}
|
||||||
|
if other_char.is_ascii_uppercase() {
|
||||||
|
other_cloned.bare_key = BareKey::Char(self_char.to_ascii_lowercase());
|
||||||
|
other_cloned.key_modifiers.insert(KeyModifier::Shift);
|
||||||
|
}
|
||||||
|
self_cloned.bare_key == other_cloned.bare_key
|
||||||
|
&& self_cloned.key_modifiers == other_cloned.key_modifiers
|
||||||
|
},
|
||||||
|
_ => self.bare_key == other.bare_key && self.key_modifiers == other.key_modifiers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for KeyWithModifier {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
match self.bare_key {
|
||||||
|
BareKey::Char(character) if character.is_ascii_uppercase() => {
|
||||||
|
let mut to_hash = self.clone();
|
||||||
|
to_hash.bare_key = BareKey::Char(character.to_ascii_lowercase());
|
||||||
|
to_hash.key_modifiers.insert(KeyModifier::Shift);
|
||||||
|
to_hash.bare_key.hash(state);
|
||||||
|
to_hash.key_modifiers.hash(state);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.bare_key.hash(state);
|
||||||
|
self.key_modifiers.hash(state);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for KeyWithModifier {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.key_modifiers.is_empty() {
|
||||||
|
write!(f, "{}", self.bare_key)
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {}",
|
||||||
|
self.key_modifiers
|
||||||
|
.iter()
|
||||||
|
.map(|m| m.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("-"),
|
||||||
|
self.bare_key
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
impl Into<Modifiers> for &KeyModifier {
|
||||||
|
fn into(self) -> Modifiers {
|
||||||
|
match self {
|
||||||
|
KeyModifier::Shift => Modifiers::SHIFT,
|
||||||
|
KeyModifier::Alt => Modifiers::ALT,
|
||||||
|
KeyModifier::Ctrl => Modifiers::CTRL,
|
||||||
|
KeyModifier::Super => Modifiers::SUPER,
|
||||||
|
KeyModifier::Hyper => Modifiers::NONE,
|
||||||
|
KeyModifier::Meta => Modifiers::NONE,
|
||||||
|
KeyModifier::CapsLock => Modifiers::NONE,
|
||||||
|
KeyModifier::NumLock => Modifiers::NONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
|
||||||
|
pub enum BareKey {
|
||||||
PageDown,
|
PageDown,
|
||||||
PageUp,
|
PageUp,
|
||||||
Left,
|
Left,
|
||||||
|
|
@ -57,149 +157,398 @@ pub enum Key {
|
||||||
Insert,
|
Insert,
|
||||||
F(u8),
|
F(u8),
|
||||||
Char(char),
|
Char(char),
|
||||||
Alt(CharOrArrow),
|
Tab,
|
||||||
Ctrl(char),
|
|
||||||
BackTab,
|
|
||||||
Null,
|
|
||||||
Esc,
|
Esc,
|
||||||
AltF(u8),
|
Enter,
|
||||||
CtrlF(u8),
|
CapsLock,
|
||||||
|
ScrollLock,
|
||||||
|
NumLock,
|
||||||
|
PrintScreen,
|
||||||
|
Pause,
|
||||||
|
Menu,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Key {
|
impl fmt::Display for BareKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
BareKey::PageDown => write!(f, "PgDn"),
|
||||||
|
BareKey::PageUp => write!(f, "PgUp"),
|
||||||
|
BareKey::Left => write!(f, "←"),
|
||||||
|
BareKey::Down => write!(f, "↓"),
|
||||||
|
BareKey::Up => write!(f, "↑"),
|
||||||
|
BareKey::Right => write!(f, "→"),
|
||||||
|
BareKey::Home => write!(f, "HOME"),
|
||||||
|
BareKey::End => write!(f, "END"),
|
||||||
|
BareKey::Backspace => write!(f, "BACKSPACE"),
|
||||||
|
BareKey::Delete => write!(f, "DEL"),
|
||||||
|
BareKey::Insert => write!(f, "INS"),
|
||||||
|
BareKey::F(index) => write!(f, "F{}", index),
|
||||||
|
BareKey::Char(' ') => write!(f, "SPACE"),
|
||||||
|
BareKey::Char(character) => write!(f, "{}", character),
|
||||||
|
BareKey::Tab => write!(f, "TAB"),
|
||||||
|
BareKey::Esc => write!(f, "ESC"),
|
||||||
|
BareKey::Enter => write!(f, "ENTER"),
|
||||||
|
BareKey::CapsLock => write!(f, "CAPSlOCK"),
|
||||||
|
BareKey::ScrollLock => write!(f, "SCROLLlOCK"),
|
||||||
|
BareKey::NumLock => write!(f, "NUMLOCK"),
|
||||||
|
BareKey::PrintScreen => write!(f, "PRINTSCREEN"),
|
||||||
|
BareKey::Pause => write!(f, "PAUSE"),
|
||||||
|
BareKey::Menu => write!(f, "MENU"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BareKey {
|
||||||
type Err = Box<dyn std::error::Error>;
|
type Err = Box<dyn std::error::Error>;
|
||||||
fn from_str(key_str: &str) -> Result<Self, Self::Err> {
|
fn from_str(key_str: &str) -> Result<Self, Self::Err> {
|
||||||
let mut modifier: Option<&str> = None;
|
match key_str.to_ascii_lowercase().as_str() {
|
||||||
let mut main_key: Option<&str> = None;
|
"pagedown" => Ok(BareKey::PageDown),
|
||||||
for (index, part) in key_str.split_ascii_whitespace().enumerate() {
|
"pageup" => Ok(BareKey::PageUp),
|
||||||
if index == 0 && (part == "Ctrl" || part == "Alt") {
|
"left" => Ok(BareKey::Left),
|
||||||
modifier = Some(part);
|
"down" => Ok(BareKey::Down),
|
||||||
} else if main_key.is_none() {
|
"up" => Ok(BareKey::Up),
|
||||||
main_key = Some(part)
|
"right" => Ok(BareKey::Right),
|
||||||
|
"home" => Ok(BareKey::Home),
|
||||||
|
"end" => Ok(BareKey::End),
|
||||||
|
"backspace" => Ok(BareKey::Backspace),
|
||||||
|
"delete" => Ok(BareKey::Delete),
|
||||||
|
"insert" => Ok(BareKey::Insert),
|
||||||
|
"f1" => Ok(BareKey::F(1)),
|
||||||
|
"f2" => Ok(BareKey::F(2)),
|
||||||
|
"f3" => Ok(BareKey::F(3)),
|
||||||
|
"f4" => Ok(BareKey::F(4)),
|
||||||
|
"f5" => Ok(BareKey::F(5)),
|
||||||
|
"f6" => Ok(BareKey::F(6)),
|
||||||
|
"f7" => Ok(BareKey::F(7)),
|
||||||
|
"f8" => Ok(BareKey::F(8)),
|
||||||
|
"f9" => Ok(BareKey::F(9)),
|
||||||
|
"f10" => Ok(BareKey::F(10)),
|
||||||
|
"f11" => Ok(BareKey::F(11)),
|
||||||
|
"f12" => Ok(BareKey::F(12)),
|
||||||
|
"tab" => Ok(BareKey::Tab),
|
||||||
|
"esc" => Ok(BareKey::Esc),
|
||||||
|
"enter" => Ok(BareKey::Enter),
|
||||||
|
"capsLock" => Ok(BareKey::CapsLock),
|
||||||
|
"scrollLock" => Ok(BareKey::ScrollLock),
|
||||||
|
"numlock" => Ok(BareKey::NumLock),
|
||||||
|
"printscreen" => Ok(BareKey::PrintScreen),
|
||||||
|
"pause" => Ok(BareKey::Pause),
|
||||||
|
"menu" => Ok(BareKey::Menu),
|
||||||
|
"space" => Ok(BareKey::Char(' ')),
|
||||||
|
_ => {
|
||||||
|
if key_str.chars().count() == 1 {
|
||||||
|
if let Some(character) = key_str.chars().next() {
|
||||||
|
return Ok(BareKey::Char(character));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err("unsupported key".into())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord, ToString,
|
||||||
|
)]
|
||||||
|
pub enum KeyModifier {
|
||||||
|
Ctrl,
|
||||||
|
Alt,
|
||||||
|
Shift,
|
||||||
|
Super,
|
||||||
|
Hyper,
|
||||||
|
Meta,
|
||||||
|
CapsLock,
|
||||||
|
NumLock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for KeyModifier {
|
||||||
|
type Err = Box<dyn std::error::Error>;
|
||||||
|
fn from_str(key_str: &str) -> Result<Self, Self::Err> {
|
||||||
|
match key_str.to_ascii_lowercase().as_str() {
|
||||||
|
"shift" => Ok(KeyModifier::Shift),
|
||||||
|
"alt" => Ok(KeyModifier::Alt),
|
||||||
|
"ctrl" => Ok(KeyModifier::Ctrl),
|
||||||
|
"super" => Ok(KeyModifier::Super),
|
||||||
|
_ => Err("unsupported modifier".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BareKey {
|
||||||
|
pub fn from_bytes_with_u(bytes: &[u8]) -> Option<Self> {
|
||||||
|
match str::from_utf8(bytes) {
|
||||||
|
Ok("27") => Some(BareKey::Esc),
|
||||||
|
Ok("13") => Some(BareKey::Enter),
|
||||||
|
Ok("9") => Some(BareKey::Tab),
|
||||||
|
Ok("127") => Some(BareKey::Backspace),
|
||||||
|
Ok("57358") => Some(BareKey::CapsLock),
|
||||||
|
Ok("57359") => Some(BareKey::ScrollLock),
|
||||||
|
Ok("57360") => Some(BareKey::NumLock),
|
||||||
|
Ok("57361") => Some(BareKey::PrintScreen),
|
||||||
|
Ok("57362") => Some(BareKey::Pause),
|
||||||
|
Ok("57363") => Some(BareKey::Menu),
|
||||||
|
Ok(num) => u8::from_str_radix(num, 10)
|
||||||
|
.ok()
|
||||||
|
.map(|n| BareKey::Char((n as char).to_ascii_lowercase())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_bytes_with_tilde(bytes: &[u8]) -> Option<Self> {
|
||||||
|
match str::from_utf8(bytes) {
|
||||||
|
Ok("2") => Some(BareKey::Insert),
|
||||||
|
Ok("3") => Some(BareKey::Delete),
|
||||||
|
Ok("5") => Some(BareKey::PageUp),
|
||||||
|
Ok("6") => Some(BareKey::PageDown),
|
||||||
|
Ok("7") => Some(BareKey::Home),
|
||||||
|
Ok("8") => Some(BareKey::End),
|
||||||
|
Ok("11") => Some(BareKey::F(1)),
|
||||||
|
Ok("12") => Some(BareKey::F(2)),
|
||||||
|
Ok("13") => Some(BareKey::F(3)),
|
||||||
|
Ok("14") => Some(BareKey::F(4)),
|
||||||
|
Ok("15") => Some(BareKey::F(5)),
|
||||||
|
Ok("17") => Some(BareKey::F(6)),
|
||||||
|
Ok("18") => Some(BareKey::F(7)),
|
||||||
|
Ok("19") => Some(BareKey::F(8)),
|
||||||
|
Ok("20") => Some(BareKey::F(9)),
|
||||||
|
Ok("21") => Some(BareKey::F(10)),
|
||||||
|
Ok("23") => Some(BareKey::F(11)),
|
||||||
|
Ok("24") => Some(BareKey::F(12)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_bytes_with_no_ending_byte(bytes: &[u8]) -> Option<Self> {
|
||||||
|
match str::from_utf8(bytes) {
|
||||||
|
Ok("1D") | Ok("D") => Some(BareKey::Left),
|
||||||
|
Ok("1C") | Ok("C") => Some(BareKey::Right),
|
||||||
|
Ok("1A") | Ok("A") => Some(BareKey::Up),
|
||||||
|
Ok("1B") | Ok("B") => Some(BareKey::Down),
|
||||||
|
Ok("1H") | Ok("H") => Some(BareKey::Home),
|
||||||
|
Ok("1F") | Ok("F") => Some(BareKey::End),
|
||||||
|
Ok("1P") | Ok("P") => Some(BareKey::F(1)),
|
||||||
|
Ok("1Q") | Ok("Q") => Some(BareKey::F(2)),
|
||||||
|
Ok("1S") | Ok("S") => Some(BareKey::F(4)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct ModifierFlags: u8 {
|
||||||
|
const SHIFT = 0b0000_0001;
|
||||||
|
const ALT = 0b0000_0010;
|
||||||
|
const CONTROL = 0b0000_0100;
|
||||||
|
const SUPER = 0b0000_1000;
|
||||||
|
const HYPER = 0b0001_0000;
|
||||||
|
const META = 0b0010_0000;
|
||||||
|
const CAPS_LOCK = 0b0100_0000;
|
||||||
|
const NUM_LOCK = 0b1000_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyModifier {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> BTreeSet<KeyModifier> {
|
||||||
|
let modifier_flags = str::from_utf8(bytes)
|
||||||
|
.ok() // convert to string: (eg. "16")
|
||||||
|
.and_then(|s| u8::from_str_radix(&s, 10).ok()) // convert to u8: (eg. 16)
|
||||||
|
.map(|s| s.saturating_sub(1)) // subtract 1: (eg. 15)
|
||||||
|
.and_then(|b| ModifierFlags::from_bits(b)); // bitflags: (0b0000_1111: Shift, Alt, Control, Super)
|
||||||
|
let mut key_modifiers = BTreeSet::new();
|
||||||
|
if let Some(modifier_flags) = modifier_flags {
|
||||||
|
for name in modifier_flags.iter() {
|
||||||
|
match name {
|
||||||
|
ModifierFlags::SHIFT => key_modifiers.insert(KeyModifier::Shift),
|
||||||
|
ModifierFlags::ALT => key_modifiers.insert(KeyModifier::Alt),
|
||||||
|
ModifierFlags::CONTROL => key_modifiers.insert(KeyModifier::Ctrl),
|
||||||
|
ModifierFlags::SUPER => key_modifiers.insert(KeyModifier::Super),
|
||||||
|
ModifierFlags::HYPER => key_modifiers.insert(KeyModifier::Hyper),
|
||||||
|
ModifierFlags::META => key_modifiers.insert(KeyModifier::Meta),
|
||||||
|
ModifierFlags::CAPS_LOCK => key_modifiers.insert(KeyModifier::CapsLock),
|
||||||
|
ModifierFlags::NUM_LOCK => key_modifiers.insert(KeyModifier::NumLock),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match (modifier, main_key) {
|
key_modifiers
|
||||||
(Some("Ctrl"), Some(main_key)) if main_key == "@" || main_key == "Space" => {
|
|
||||||
Ok(Key::Char('\x00'))
|
|
||||||
},
|
|
||||||
(Some("Ctrl"), Some(main_key)) => {
|
|
||||||
parse_main_key(main_key, key_str, Key::Ctrl, Key::CtrlF)
|
|
||||||
},
|
|
||||||
(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))),
|
|
||||||
_ => parse_main_key(
|
|
||||||
main_key,
|
|
||||||
key_str,
|
|
||||||
|c| Key::Alt(CharOrArrow::Char(c)),
|
|
||||||
Key::AltF,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(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),
|
|
||||||
_ => parse_main_key(main_key, key_str, Key::Char, Key::F),
|
|
||||||
},
|
|
||||||
_ => Err(format!("Failed to parse key: {}", key_str).into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_main_key(
|
impl KeyWithModifier {
|
||||||
main_key: &str,
|
pub fn new(bare_key: BareKey) -> Self {
|
||||||
key_str: &str,
|
KeyWithModifier {
|
||||||
to_char_key: impl FnOnce(char) -> Key,
|
bare_key,
|
||||||
to_fn_key: impl FnOnce(u8) -> Key,
|
key_modifiers: BTreeSet::new(),
|
||||||
) -> Result<Key, Box<dyn std::error::Error>> {
|
}
|
||||||
let mut key_chars = main_key.chars();
|
}
|
||||||
let key_count = main_key.chars().count();
|
pub fn new_with_modifiers(bare_key: BareKey, key_modifiers: BTreeSet<KeyModifier>) -> Self {
|
||||||
if key_count == 1 {
|
KeyWithModifier {
|
||||||
let key_char = key_chars.next().unwrap();
|
bare_key,
|
||||||
Ok(to_char_key(key_char))
|
key_modifiers,
|
||||||
} else if key_count > 1 {
|
}
|
||||||
if let Some(first_char) = key_chars.next() {
|
}
|
||||||
if first_char == 'F' {
|
pub fn with_shift_modifier(mut self) -> Self {
|
||||||
let f_index: String = key_chars.collect();
|
self.key_modifiers.insert(KeyModifier::Shift);
|
||||||
let f_index: u8 = f_index
|
self
|
||||||
.parse()
|
}
|
||||||
.map_err(|e| format!("Failed to parse F index: {}", e))?;
|
pub fn with_alt_modifier(mut self) -> Self {
|
||||||
if f_index >= 1 && f_index <= 12 {
|
self.key_modifiers.insert(KeyModifier::Alt);
|
||||||
return Ok(to_fn_key(f_index));
|
self
|
||||||
}
|
}
|
||||||
|
pub fn with_ctrl_modifier(mut self) -> Self {
|
||||||
|
self.key_modifiers.insert(KeyModifier::Ctrl);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_super_modifier(mut self) -> Self {
|
||||||
|
self.key_modifiers.insert(KeyModifier::Super);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn from_bytes_with_u(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
|
||||||
|
// CSI number ; modifiers u
|
||||||
|
let bare_key = BareKey::from_bytes_with_u(number_bytes);
|
||||||
|
match bare_key {
|
||||||
|
Some(bare_key) => {
|
||||||
|
let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
|
||||||
|
Some(KeyWithModifier {
|
||||||
|
bare_key,
|
||||||
|
key_modifiers,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_bytes_with_tilde(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
|
||||||
|
// CSI number ; modifiers ~
|
||||||
|
let bare_key = BareKey::from_bytes_with_tilde(number_bytes);
|
||||||
|
match bare_key {
|
||||||
|
Some(bare_key) => {
|
||||||
|
let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
|
||||||
|
Some(KeyWithModifier {
|
||||||
|
bare_key,
|
||||||
|
key_modifiers,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_bytes_with_no_ending_byte(
|
||||||
|
number_bytes: &[u8],
|
||||||
|
modifier_bytes: &[u8],
|
||||||
|
) -> Option<Self> {
|
||||||
|
// CSI 1; modifiers [ABCDEFHPQS]
|
||||||
|
let bare_key = BareKey::from_bytes_with_no_ending_byte(number_bytes);
|
||||||
|
match bare_key {
|
||||||
|
Some(bare_key) => {
|
||||||
|
let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
|
||||||
|
Some(KeyWithModifier {
|
||||||
|
bare_key,
|
||||||
|
key_modifiers,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn strip_common_modifiers(&self, common_modifiers: &Vec<KeyModifier>) -> Self {
|
||||||
|
let common_modifiers: BTreeSet<&KeyModifier> = common_modifiers.into_iter().collect();
|
||||||
|
KeyWithModifier {
|
||||||
|
bare_key: self.bare_key.clone(),
|
||||||
|
key_modifiers: self
|
||||||
|
.key_modifiers
|
||||||
|
.iter()
|
||||||
|
.filter(|m| !common_modifiers.contains(m))
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_key_without_modifier(&self, key: BareKey) -> bool {
|
||||||
|
self.bare_key == key && self.key_modifiers.is_empty()
|
||||||
|
}
|
||||||
|
pub fn is_key_with_ctrl_modifier(&self, key: BareKey) -> bool {
|
||||||
|
self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Ctrl)
|
||||||
|
}
|
||||||
|
pub fn is_key_with_alt_modifier(&self, key: BareKey) -> bool {
|
||||||
|
self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Alt)
|
||||||
|
}
|
||||||
|
pub fn is_key_with_shift_modifier(&self, key: BareKey) -> bool {
|
||||||
|
self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Shift)
|
||||||
|
}
|
||||||
|
pub fn is_key_with_super_modifier(&self, key: BareKey) -> bool {
|
||||||
|
self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Super)
|
||||||
|
}
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub fn to_termwiz_modifiers(&self) -> Modifiers {
|
||||||
|
let mut modifiers = Modifiers::empty();
|
||||||
|
for modifier in &self.key_modifiers {
|
||||||
|
modifiers.set(modifier.into(), true);
|
||||||
|
}
|
||||||
|
modifiers
|
||||||
|
}
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub fn to_termwiz_keycode(&self) -> KeyCode {
|
||||||
|
match self.bare_key {
|
||||||
|
BareKey::PageDown => KeyCode::PageDown,
|
||||||
|
BareKey::PageUp => KeyCode::PageUp,
|
||||||
|
BareKey::Left => KeyCode::LeftArrow,
|
||||||
|
BareKey::Down => KeyCode::DownArrow,
|
||||||
|
BareKey::Up => KeyCode::UpArrow,
|
||||||
|
BareKey::Right => KeyCode::RightArrow,
|
||||||
|
BareKey::Home => KeyCode::Home,
|
||||||
|
BareKey::End => KeyCode::End,
|
||||||
|
BareKey::Backspace => KeyCode::Backspace,
|
||||||
|
BareKey::Delete => KeyCode::Delete,
|
||||||
|
BareKey::Insert => KeyCode::Insert,
|
||||||
|
BareKey::F(index) => KeyCode::Function(index),
|
||||||
|
BareKey::Char(character) => KeyCode::Char(character),
|
||||||
|
BareKey::Tab => KeyCode::Tab,
|
||||||
|
BareKey::Esc => KeyCode::Escape,
|
||||||
|
BareKey::Enter => KeyCode::Enter,
|
||||||
|
BareKey::CapsLock => KeyCode::CapsLock,
|
||||||
|
BareKey::ScrollLock => KeyCode::ScrollLock,
|
||||||
|
BareKey::NumLock => KeyCode::NumLock,
|
||||||
|
BareKey::PrintScreen => KeyCode::PrintScreen,
|
||||||
|
BareKey::Pause => KeyCode::Pause,
|
||||||
|
BareKey::Menu => KeyCode::Menu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub fn serialize_non_kitty(&self) -> Option<String> {
|
||||||
|
let modifiers = self.to_termwiz_modifiers();
|
||||||
|
let key_code_encode_modes = KeyCodeEncodeModes {
|
||||||
|
encoding: KeyboardEncoding::Xterm,
|
||||||
|
// all these flags are false because they have been dealt with before this
|
||||||
|
// serialization
|
||||||
|
application_cursor_keys: false,
|
||||||
|
newline_mode: false,
|
||||||
|
modify_other_keys: None,
|
||||||
|
};
|
||||||
|
self.to_termwiz_keycode()
|
||||||
|
.encode(modifiers, key_code_encode_modes, true)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub fn serialize_kitty(&self) -> Option<String> {
|
||||||
|
let modifiers = self.to_termwiz_modifiers();
|
||||||
|
let key_code_encode_modes = KeyCodeEncodeModes {
|
||||||
|
encoding: KeyboardEncoding::Kitty(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES),
|
||||||
|
// all these flags are false because they have been dealt with before this
|
||||||
|
// serialization
|
||||||
|
application_cursor_keys: false,
|
||||||
|
newline_mode: false,
|
||||||
|
modify_other_keys: None,
|
||||||
|
};
|
||||||
|
self.to_termwiz_keycode()
|
||||||
|
.encode(modifiers, key_code_encode_modes, true)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub fn has_no_modifiers(&self) -> bool {
|
||||||
|
self.key_modifiers.is_empty()
|
||||||
|
}
|
||||||
|
pub fn has_modifiers(&self, modifiers: &[KeyModifier]) -> bool {
|
||||||
|
for modifier in modifiers {
|
||||||
|
if !self.key_modifiers.contains(modifier) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(format!("Failed to parse key: {}", key_str).into())
|
true
|
||||||
} else {
|
|
||||||
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 {
|
|
||||||
Key::Backspace => write!(f, "BACKSPACE"),
|
|
||||||
Key::Left => write!(f, "{}", Direction::Left),
|
|
||||||
Key::Right => write!(f, "{}", Direction::Right),
|
|
||||||
Key::Up => write!(f, "{}", Direction::Up),
|
|
||||||
Key::Down => write!(f, "{}", Direction::Down),
|
|
||||||
Key::Home => write!(f, "HOME"),
|
|
||||||
Key::End => write!(f, "END"),
|
|
||||||
Key::PageUp => write!(f, "PgUp"),
|
|
||||||
Key::PageDown => write!(f, "PgDn"),
|
|
||||||
Key::BackTab => write!(f, "TAB"),
|
|
||||||
Key::Delete => write!(f, "DEL"),
|
|
||||||
Key::Insert => write!(f, "INS"),
|
|
||||||
Key::F(n) => write!(f, "F{}", n),
|
|
||||||
Key::Char(c) => match c {
|
|
||||||
'\n' => write!(f, "ENTER"),
|
|
||||||
'\t' => write!(f, "TAB"),
|
|
||||||
' ' => write!(f, "SPACE"),
|
|
||||||
'\x00' => write!(f, "Ctrl+SPACE"),
|
|
||||||
_ => write!(f, "{}", c),
|
|
||||||
},
|
|
||||||
Key::Alt(c) => write!(f, "Alt+{}", c),
|
|
||||||
Key::Ctrl(c) => write!(f, "Ctrl+{}", Key::Char(*c)),
|
|
||||||
Key::AltF(n) => write!(f, "Alt+F{}", n),
|
|
||||||
Key::CtrlF(n) => write!(f, "Ctrl+F{}", n),
|
|
||||||
Key::Null => write!(f, "NULL"),
|
|
||||||
Key::Esc => write!(f, "ESC"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum CharOrArrow {
|
|
||||||
Char(char),
|
|
||||||
Direction(Direction),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for CharOrArrow {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
CharOrArrow::Char(c) => write!(f, "{}", Key::Char(*c)),
|
|
||||||
CharOrArrow::Direction(d) => write!(f, "{}", d),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,7 +842,7 @@ pub enum Event {
|
||||||
TabUpdate(Vec<TabInfo>),
|
TabUpdate(Vec<TabInfo>),
|
||||||
PaneUpdate(PaneManifest),
|
PaneUpdate(PaneManifest),
|
||||||
/// A key was pressed while the user is focused on this plugin's pane
|
/// A key was pressed while the user is focused on this plugin's pane
|
||||||
Key(Key),
|
Key(KeyWithModifier),
|
||||||
/// A mouse event happened while the user is focused on this plugin's pane
|
/// A mouse event happened while the user is focused on this plugin's pane
|
||||||
Mouse(Mouse),
|
Mouse(Mouse),
|
||||||
/// A timer expired set by the `set_timeout` method exported by `zellij-tile`.
|
/// A timer expired set by the `set_timeout` method exported by `zellij-tile`.
|
||||||
|
|
@ -756,7 +1105,7 @@ pub struct Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Poor devs hashtable since HashTable can't derive `Default`...
|
// FIXME: Poor devs hashtable since HashTable can't derive `Default`...
|
||||||
pub type KeybindsVec = Vec<(InputMode, Vec<(Key, Vec<Action>)>)>;
|
pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
|
||||||
|
|
||||||
/// Provides information helpful in rendering the Zellij controls for UI bars
|
/// Provides information helpful in rendering the Zellij controls for UI bars
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
@ -769,11 +1118,11 @@ pub struct ModeInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModeInfo {
|
impl ModeInfo {
|
||||||
pub fn get_mode_keybinds(&self) -> Vec<(Key, Vec<Action>)> {
|
pub fn get_mode_keybinds(&self) -> Vec<(KeyWithModifier, Vec<Action>)> {
|
||||||
self.get_keybinds_for_mode(self.mode)
|
self.get_keybinds_for_mode(self.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(Key, Vec<Action>)> {
|
pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(KeyWithModifier, Vec<Action>)> {
|
||||||
for (vec_mode, map) in &self.keybinds {
|
for (vec_mode, map) in &self.keybinds {
|
||||||
if mode == *vec_mode {
|
if mode == *vec_mode {
|
||||||
return map.to_vec();
|
return map.to_vec();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use super::layout::{
|
||||||
SwapFloatingLayout, SwapTiledLayout, TiledPaneLayout,
|
SwapFloatingLayout, SwapTiledLayout, TiledPaneLayout,
|
||||||
};
|
};
|
||||||
use crate::cli::CliAction;
|
use crate::cli::CliAction;
|
||||||
use crate::data::{Direction, Resize};
|
use crate::data::{Direction, KeyWithModifier, Resize};
|
||||||
use crate::data::{FloatingPaneCoordinates, InputMode};
|
use crate::data::{FloatingPaneCoordinates, InputMode};
|
||||||
use crate::home::{find_default_config_dir, get_layout_dir};
|
use crate::home::{find_default_config_dir, get_layout_dir};
|
||||||
use crate::input::config::{Config, ConfigError, KdlError};
|
use crate::input::config::{Config, ConfigError, KdlError};
|
||||||
|
|
@ -102,7 +102,7 @@ pub enum Action {
|
||||||
/// Quit Zellij.
|
/// Quit Zellij.
|
||||||
Quit,
|
Quit,
|
||||||
/// Write to the terminal.
|
/// Write to the terminal.
|
||||||
Write(Vec<u8>),
|
Write(Option<KeyWithModifier>, Vec<u8>, bool), // bool -> is_kitty_keyboard_protocol
|
||||||
/// Write Characters to the terminal.
|
/// Write Characters to the terminal.
|
||||||
WriteChars(String),
|
WriteChars(String),
|
||||||
/// Switch to the specified input mode.
|
/// Switch to the specified input mode.
|
||||||
|
|
@ -317,7 +317,7 @@ impl Action {
|
||||||
config: Option<Config>,
|
config: Option<Config>,
|
||||||
) -> Result<Vec<Action>, String> {
|
) -> Result<Vec<Action>, String> {
|
||||||
match cli_action {
|
match cli_action {
|
||||||
CliAction::Write { bytes } => Ok(vec![Action::Write(bytes)]),
|
CliAction::Write { bytes } => Ok(vec![Action::Write(None, bytes, false)]),
|
||||||
CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]),
|
CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]),
|
||||||
CliAction::Resize { resize, direction } => Ok(vec![Action::Resize(resize, direction)]),
|
CliAction::Resize { resize, direction } => Ok(vec![Action::Resize(resize, direction)]),
|
||||||
CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]),
|
CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
use super::actions::Action;
|
use super::actions::Action;
|
||||||
use crate::data::{InputMode, Key, KeybindsVec};
|
use crate::data::{InputMode, KeyWithModifier, KeybindsVec};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Used in the config struct
|
/// Used in the config struct
|
||||||
#[derive(Clone, PartialEq, Deserialize, Serialize, Default)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize, Default)]
|
||||||
pub struct Keybinds(pub HashMap<InputMode, HashMap<Key, Vec<Action>>>);
|
pub struct Keybinds(pub HashMap<InputMode, HashMap<KeyWithModifier, Vec<Action>>>);
|
||||||
|
|
||||||
impl fmt::Debug for Keybinds {
|
impl fmt::Debug for Keybinds {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|
@ -25,7 +25,11 @@ impl fmt::Debug for Keybinds {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keybinds {
|
impl Keybinds {
|
||||||
pub fn get_actions_for_key_in_mode(&self, mode: &InputMode, key: &Key) -> Option<&Vec<Action>> {
|
pub fn get_actions_for_key_in_mode(
|
||||||
|
&self,
|
||||||
|
mode: &InputMode,
|
||||||
|
key: &KeyWithModifier,
|
||||||
|
) -> Option<&Vec<Action>> {
|
||||||
self.0
|
self.0
|
||||||
.get(mode)
|
.get(mode)
|
||||||
.and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key))
|
.and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key))
|
||||||
|
|
@ -33,21 +37,40 @@ impl Keybinds {
|
||||||
pub fn get_actions_for_key_in_mode_or_default_action(
|
pub fn get_actions_for_key_in_mode_or_default_action(
|
||||||
&self,
|
&self,
|
||||||
mode: &InputMode,
|
mode: &InputMode,
|
||||||
key: &Key,
|
key_with_modifier: &KeyWithModifier,
|
||||||
raw_bytes: Vec<u8>,
|
raw_bytes: Vec<u8>,
|
||||||
|
key_is_kitty_protocol: bool,
|
||||||
) -> Vec<Action> {
|
) -> Vec<Action> {
|
||||||
self.0
|
self.0
|
||||||
.get(mode)
|
.get(mode)
|
||||||
.and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key))
|
.and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key_with_modifier))
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| vec![self.default_action_for_mode(mode, raw_bytes)])
|
.unwrap_or_else(|| {
|
||||||
|
vec![self.default_action_for_mode(
|
||||||
|
mode,
|
||||||
|
Some(key_with_modifier),
|
||||||
|
raw_bytes,
|
||||||
|
key_is_kitty_protocol,
|
||||||
|
)]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub fn get_input_mode_mut(&mut self, input_mode: &InputMode) -> &mut HashMap<Key, Vec<Action>> {
|
pub fn get_input_mode_mut(
|
||||||
|
&mut self,
|
||||||
|
input_mode: &InputMode,
|
||||||
|
) -> &mut HashMap<KeyWithModifier, Vec<Action>> {
|
||||||
self.0.entry(*input_mode).or_insert_with(HashMap::new)
|
self.0.entry(*input_mode).or_insert_with(HashMap::new)
|
||||||
}
|
}
|
||||||
pub fn default_action_for_mode(&self, mode: &InputMode, raw_bytes: Vec<u8>) -> Action {
|
pub fn default_action_for_mode(
|
||||||
|
&self,
|
||||||
|
mode: &InputMode,
|
||||||
|
key_with_modifier: Option<&KeyWithModifier>,
|
||||||
|
raw_bytes: Vec<u8>,
|
||||||
|
key_is_kitty_protocol: bool,
|
||||||
|
) -> Action {
|
||||||
match *mode {
|
match *mode {
|
||||||
InputMode::Normal | InputMode::Locked => Action::Write(raw_bytes),
|
InputMode::Normal | InputMode::Locked => {
|
||||||
|
Action::Write(key_with_modifier.cloned(), raw_bytes, key_is_kitty_protocol)
|
||||||
|
},
|
||||||
InputMode::RenameTab => Action::TabNameInput(raw_bytes),
|
InputMode::RenameTab => Action::TabNameInput(raw_bytes),
|
||||||
InputMode::RenamePane => Action::PaneNameInput(raw_bytes),
|
InputMode::RenamePane => Action::PaneNameInput(raw_bytes),
|
||||||
InputMode::EnterSearch => Action::SearchInput(raw_bytes),
|
InputMode::EnterSearch => Action::SearchInput(raw_bytes),
|
||||||
|
|
@ -57,7 +80,7 @@ impl Keybinds {
|
||||||
pub fn to_keybinds_vec(&self) -> KeybindsVec {
|
pub fn to_keybinds_vec(&self) -> KeybindsVec {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
for (mode, mode_binds) in &self.0 {
|
for (mode, mode_binds) in &self.0 {
|
||||||
let mut mode_binds_vec: Vec<(Key, Vec<Action>)> = vec![];
|
let mut mode_binds_vec: Vec<(KeyWithModifier, Vec<Action>)> = vec![];
|
||||||
for (key, actions) in mode_binds {
|
for (key, actions) in mode_binds {
|
||||||
mode_binds_vec.push((key.clone(), actions.clone()));
|
mode_binds_vec.push((key.clone(), actions.clone()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,14 @@ pub use not_wasm::*;
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
mod not_wasm {
|
mod not_wasm {
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{CharOrArrow, Direction, InputMode, Key, ModeInfo, PluginCapabilities},
|
data::{BareKey, InputMode, KeyModifier, KeyWithModifier, ModeInfo, PluginCapabilities},
|
||||||
envs,
|
envs,
|
||||||
ipc::ClientAttributes,
|
ipc::ClientAttributes,
|
||||||
};
|
};
|
||||||
use termwiz::input::{InputEvent, InputParser, KeyCode, KeyEvent, Modifiers};
|
use termwiz::input::{InputEvent, InputParser, KeyCode, KeyEvent, Modifiers};
|
||||||
|
|
||||||
use super::keybinds::Keybinds;
|
use super::keybinds::Keybinds;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
/// Creates a [`ModeInfo`] struct indicating the current [`InputMode`] and its keybinds
|
/// Creates a [`ModeInfo`] struct indicating the current [`InputMode`] and its keybinds
|
||||||
/// (as pairs of [`String`]s).
|
/// (as pairs of [`String`]s).
|
||||||
|
|
@ -45,7 +46,8 @@ mod not_wasm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
|
// used for parsing keys to plugins
|
||||||
|
pub fn parse_keys(input_bytes: &[u8]) -> Vec<KeyWithModifier> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
let mut input_parser = InputParser::new(); // this is the termwiz InputParser
|
let mut input_parser = InputParser::new(); // this is the termwiz InputParser
|
||||||
let maybe_more = false;
|
let maybe_more = false;
|
||||||
|
|
@ -58,9 +60,9 @@ mod not_wasm {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_is_bound(key: Key, keybinds: &Keybinds, mode: &InputMode) -> bool {
|
fn key_is_bound(key: &KeyWithModifier, keybinds: &Keybinds, mode: &InputMode) -> bool {
|
||||||
keybinds
|
keybinds
|
||||||
.get_actions_for_key_in_mode(mode, &key)
|
.get_actions_for_key_in_mode(mode, key)
|
||||||
.map_or(false, |actions| !actions.is_empty())
|
.map_or(false, |actions| !actions.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,81 +72,68 @@ mod not_wasm {
|
||||||
event: KeyEvent,
|
event: KeyEvent,
|
||||||
raw_bytes: &[u8],
|
raw_bytes: &[u8],
|
||||||
keybinds_mode: Option<(&Keybinds, &InputMode)>,
|
keybinds_mode: Option<(&Keybinds, &InputMode)>,
|
||||||
) -> Key {
|
) -> KeyWithModifier {
|
||||||
let modifiers = event.modifiers;
|
let termwiz_modifiers = event.modifiers;
|
||||||
|
|
||||||
// *** THIS IS WHERE WE SHOULD WORK AROUND ISSUES WITH TERMWIZ ***
|
// *** THIS IS WHERE WE SHOULD WORK AROUND ISSUES WITH TERMWIZ ***
|
||||||
if raw_bytes == [8] {
|
if raw_bytes == [8] {
|
||||||
return Key::Ctrl('h');
|
return KeyWithModifier::new(BareKey::Char('h')).with_ctrl_modifier();
|
||||||
};
|
};
|
||||||
|
|
||||||
if raw_bytes == [10] {
|
if raw_bytes == [10] {
|
||||||
if let Some((keybinds, mode)) = keybinds_mode {
|
if let Some((keybinds, mode)) = keybinds_mode {
|
||||||
if key_is_bound(Key::Ctrl('j'), keybinds, mode) {
|
let ctrl_j = KeyWithModifier::new(BareKey::Char('j')).with_ctrl_modifier();
|
||||||
return Key::Ctrl('j');
|
if key_is_bound(&ctrl_j, keybinds, mode) {
|
||||||
|
return ctrl_j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut modifiers = BTreeSet::new();
|
||||||
|
if termwiz_modifiers.contains(Modifiers::CTRL) {
|
||||||
|
modifiers.insert(KeyModifier::Ctrl);
|
||||||
|
}
|
||||||
|
if termwiz_modifiers.contains(Modifiers::ALT) {
|
||||||
|
modifiers.insert(KeyModifier::Alt);
|
||||||
|
}
|
||||||
|
if termwiz_modifiers.contains(Modifiers::SHIFT) {
|
||||||
|
modifiers.insert(KeyModifier::Shift);
|
||||||
|
}
|
||||||
|
|
||||||
match event.key {
|
match event.key {
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
if modifiers.contains(Modifiers::CTRL) {
|
if c == '\0' {
|
||||||
Key::Ctrl(c.to_lowercase().next().unwrap_or_default())
|
// NUL character, probably ctrl-space
|
||||||
} else if modifiers.contains(Modifiers::ALT) {
|
KeyWithModifier::new(BareKey::Char(' ')).with_ctrl_modifier()
|
||||||
Key::Alt(CharOrArrow::Char(c))
|
|
||||||
} else {
|
} else {
|
||||||
Key::Char(c)
|
KeyWithModifier::new_with_modifiers(BareKey::Char(c), modifiers)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
KeyCode::Backspace => Key::Backspace,
|
KeyCode::Backspace => {
|
||||||
|
KeyWithModifier::new_with_modifiers(BareKey::Backspace, modifiers)
|
||||||
|
},
|
||||||
KeyCode::LeftArrow | KeyCode::ApplicationLeftArrow => {
|
KeyCode::LeftArrow | KeyCode::ApplicationLeftArrow => {
|
||||||
if modifiers.contains(Modifiers::ALT) {
|
KeyWithModifier::new_with_modifiers(BareKey::Left, modifiers)
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Left))
|
|
||||||
} else {
|
|
||||||
Key::Left
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
KeyCode::RightArrow | KeyCode::ApplicationRightArrow => {
|
KeyCode::RightArrow | KeyCode::ApplicationRightArrow => {
|
||||||
if modifiers.contains(Modifiers::ALT) {
|
KeyWithModifier::new_with_modifiers(BareKey::Right, modifiers)
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Right))
|
|
||||||
} else {
|
|
||||||
Key::Right
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
KeyCode::UpArrow | KeyCode::ApplicationUpArrow => {
|
KeyCode::UpArrow | KeyCode::ApplicationUpArrow => {
|
||||||
if modifiers.contains(Modifiers::ALT) {
|
KeyWithModifier::new_with_modifiers(BareKey::Up, modifiers)
|
||||||
//Key::AltPlusUpArrow
|
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Up))
|
|
||||||
} else {
|
|
||||||
Key::Up
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
KeyCode::DownArrow | KeyCode::ApplicationDownArrow => {
|
KeyCode::DownArrow | KeyCode::ApplicationDownArrow => {
|
||||||
if modifiers.contains(Modifiers::ALT) {
|
KeyWithModifier::new_with_modifiers(BareKey::Down, modifiers)
|
||||||
Key::Alt(CharOrArrow::Direction(Direction::Down))
|
|
||||||
} else {
|
|
||||||
Key::Down
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
KeyCode::Home => Key::Home,
|
KeyCode::Home => KeyWithModifier::new_with_modifiers(BareKey::Home, modifiers),
|
||||||
KeyCode::End => Key::End,
|
KeyCode::End => KeyWithModifier::new_with_modifiers(BareKey::End, modifiers),
|
||||||
KeyCode::PageUp => Key::PageUp,
|
KeyCode::PageUp => KeyWithModifier::new_with_modifiers(BareKey::PageUp, modifiers),
|
||||||
KeyCode::PageDown => Key::PageDown,
|
KeyCode::PageDown => KeyWithModifier::new_with_modifiers(BareKey::PageDown, modifiers),
|
||||||
KeyCode::Tab => Key::BackTab, // TODO: ???
|
KeyCode::Tab => KeyWithModifier::new_with_modifiers(BareKey::Tab, modifiers),
|
||||||
KeyCode::Delete => Key::Delete,
|
KeyCode::Delete => KeyWithModifier::new_with_modifiers(BareKey::Delete, modifiers),
|
||||||
KeyCode::Insert => Key::Insert,
|
KeyCode::Insert => KeyWithModifier::new_with_modifiers(BareKey::Insert, modifiers),
|
||||||
KeyCode::Function(n) => {
|
KeyCode::Function(n) => KeyWithModifier::new_with_modifiers(BareKey::F(n), modifiers),
|
||||||
if modifiers.contains(Modifiers::ALT) {
|
KeyCode::Escape => KeyWithModifier::new_with_modifiers(BareKey::Esc, modifiers),
|
||||||
Key::AltF(n)
|
KeyCode::Enter => KeyWithModifier::new_with_modifiers(BareKey::Enter, modifiers),
|
||||||
} else if modifiers.contains(Modifiers::CTRL) {
|
_ => KeyWithModifier::new(BareKey::Esc),
|
||||||
Key::CtrlF(n)
|
|
||||||
} else {
|
|
||||||
Key::F(n)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
KeyCode::Escape => Key::Esc,
|
|
||||||
KeyCode::Enter => Key::Char('\n'),
|
|
||||||
_ => Key::Esc, // there are other keys we can implement here, but we might need additional terminal support to implement them, not just exhausting this enum
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,12 @@ pub struct Options {
|
||||||
/// If true, will disable writing session metadata to disk
|
/// If true, will disable writing session metadata to disk
|
||||||
#[clap(long, value_parser)]
|
#[clap(long, value_parser)]
|
||||||
pub disable_session_metadata: Option<bool>,
|
pub disable_session_metadata: Option<bool>,
|
||||||
|
|
||||||
|
/// Whether to enable support for the Kitty keyboard protocol (must also be supported by the
|
||||||
|
/// host terminal), defaults to true if the terminal supports it
|
||||||
|
#[clap(long, value_parser)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub support_kitty_keyboard_protocol: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
|
#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
@ -230,6 +236,9 @@ impl Options {
|
||||||
let disable_session_metadata = other
|
let disable_session_metadata = other
|
||||||
.disable_session_metadata
|
.disable_session_metadata
|
||||||
.or(self.disable_session_metadata);
|
.or(self.disable_session_metadata);
|
||||||
|
let support_kitty_keyboard_protocol = other
|
||||||
|
.support_kitty_keyboard_protocol
|
||||||
|
.or(self.support_kitty_keyboard_protocol);
|
||||||
|
|
||||||
Options {
|
Options {
|
||||||
simplified_ui,
|
simplified_ui,
|
||||||
|
|
@ -258,6 +267,7 @@ impl Options {
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
serialization_interval,
|
serialization_interval,
|
||||||
disable_session_metadata,
|
disable_session_metadata,
|
||||||
|
support_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +323,9 @@ impl Options {
|
||||||
let disable_session_metadata = other
|
let disable_session_metadata = other
|
||||||
.disable_session_metadata
|
.disable_session_metadata
|
||||||
.or(self.disable_session_metadata);
|
.or(self.disable_session_metadata);
|
||||||
|
let support_kitty_keyboard_protocol = other
|
||||||
|
.support_kitty_keyboard_protocol
|
||||||
|
.or(self.support_kitty_keyboard_protocol);
|
||||||
|
|
||||||
Options {
|
Options {
|
||||||
simplified_ui,
|
simplified_ui,
|
||||||
|
|
@ -341,6 +354,7 @@ impl Options {
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
serialization_interval,
|
serialization_interval,
|
||||||
disable_session_metadata,
|
disable_session_metadata,
|
||||||
|
support_kitty_keyboard_protocol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,6 +419,7 @@ impl From<CliOptions> for Options {
|
||||||
scrollback_lines_to_serialize: opts.scrollback_lines_to_serialize,
|
scrollback_lines_to_serialize: opts.scrollback_lines_to_serialize,
|
||||||
styled_underlines: opts.styled_underlines,
|
styled_underlines: opts.styled_underlines,
|
||||||
serialization_interval: opts.serialization_interval,
|
serialization_interval: opts.serialization_interval,
|
||||||
|
support_kitty_keyboard_protocol: opts.support_kitty_keyboard_protocol,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::super::actions::*;
|
use super::super::actions::*;
|
||||||
use super::super::keybinds::*;
|
use super::super::keybinds::*;
|
||||||
use crate::data::{self, CharOrArrow, Direction, Key};
|
use crate::data::{BareKey, Direction, KeyWithModifier};
|
||||||
use crate::input::config::Config;
|
use crate::input::config::Config;
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
@ -15,9 +15,10 @@ fn can_define_keybindings_in_configfile() {
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
let ctrl_g_normal_mode_action = config
|
let ctrl_g_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&InputMode::Normal,
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctrl_g_normal_mode_action,
|
ctrl_g_normal_mode_action,
|
||||||
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
||||||
|
|
@ -37,11 +38,12 @@ fn can_define_multiple_keybinds_for_same_action() {
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
let alt_h_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
let alt_h_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
&InputMode::Normal,
|
&InputMode::Normal,
|
||||||
&Key::Alt(CharOrArrow::Direction(data::Direction::Left)),
|
&KeyWithModifier::new(BareKey::Left).with_alt_modifier(),
|
||||||
|
);
|
||||||
|
let alt_left_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
|
&InputMode::Normal,
|
||||||
|
&KeyWithModifier::new(BareKey::Char('h')).with_alt_modifier(),
|
||||||
);
|
);
|
||||||
let alt_left_normal_mode_action = config
|
|
||||||
.keybinds
|
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Alt(CharOrArrow::Char('h')));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
alt_h_normal_mode_action,
|
alt_h_normal_mode_action,
|
||||||
Some(&vec![Action::MoveFocusOrTab(Direction::Left)]),
|
Some(&vec![Action::MoveFocusOrTab(Direction::Left)]),
|
||||||
|
|
@ -66,7 +68,7 @@ fn can_define_series_of_actions_for_same_keybinding() {
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
z_in_pane_mode,
|
z_in_pane_mode,
|
||||||
Some(&vec![
|
Some(&vec![
|
||||||
|
|
@ -90,7 +92,7 @@ fn keybindings_bind_order_is_preserved() {
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
z_in_pane_mode,
|
z_in_pane_mode,
|
||||||
Some(&vec![Action::SwitchToMode(InputMode::Resize)]),
|
Some(&vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
|
|
@ -111,10 +113,10 @@ fn uppercase_and_lowercase_keybindings_are_distinct() {
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
let uppercase_z_in_pane_mode = config
|
let uppercase_z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('Z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('Z')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
z_in_pane_mode,
|
z_in_pane_mode,
|
||||||
Some(&vec![
|
Some(&vec![
|
||||||
|
|
@ -150,7 +152,7 @@ fn can_override_keybindings() {
|
||||||
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
z_in_pane_mode,
|
z_in_pane_mode,
|
||||||
Some(&vec![Action::SwitchToMode(InputMode::Resize)]),
|
Some(&vec![Action::SwitchToMode(InputMode::Resize)]),
|
||||||
|
|
@ -180,10 +182,10 @@ fn can_add_to_default_keybindings() {
|
||||||
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
let r_in_pane_mode = config
|
let r_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('r')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
z_in_pane_mode,
|
z_in_pane_mode,
|
||||||
Some(&vec![
|
Some(&vec![
|
||||||
|
|
@ -223,18 +225,20 @@ fn can_clear_default_keybindings() {
|
||||||
"#;
|
"#;
|
||||||
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
||||||
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
||||||
let ctrl_g_normal_mode_action = config
|
let ctrl_g_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&InputMode::Normal,
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
let ctrl_r_in_normal_mode = config
|
let ctrl_r_in_normal_mode = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&InputMode::Normal,
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('r'));
|
&KeyWithModifier::new(BareKey::Char('r')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
let r_in_pane_mode = config
|
let r_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('r')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctrl_g_normal_mode_action, None,
|
ctrl_g_normal_mode_action, None,
|
||||||
"Keybinding from normal mode in default config cleared"
|
"Keybinding from normal mode in default config cleared"
|
||||||
|
|
@ -276,15 +280,16 @@ fn can_clear_default_keybindings_per_single_mode() {
|
||||||
"#;
|
"#;
|
||||||
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
||||||
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
||||||
let ctrl_g_normal_mode_action = config
|
let ctrl_g_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&InputMode::Normal,
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
let r_in_pane_mode = config
|
let r_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('r')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctrl_g_normal_mode_action,
|
ctrl_g_normal_mode_action,
|
||||||
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
||||||
|
|
@ -325,21 +330,23 @@ fn can_unbind_multiple_keys_globally() {
|
||||||
"#;
|
"#;
|
||||||
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
||||||
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
||||||
let ctrl_g_normal_mode_action = config
|
let ctrl_g_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&InputMode::Normal,
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
let ctrl_g_pane_mode_action = config
|
);
|
||||||
.keybinds
|
let ctrl_g_pane_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g'));
|
&InputMode::Pane,
|
||||||
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
let r_in_pane_mode = config
|
let r_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('r')));
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
let t_in_pane_mode = config
|
let t_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('t'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('t')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctrl_g_normal_mode_action, None,
|
ctrl_g_normal_mode_action, None,
|
||||||
"First keybind uncleared in one mode"
|
"First keybind uncleared in one mode"
|
||||||
|
|
@ -385,21 +392,23 @@ fn can_unbind_multiple_keys_per_single_mode() {
|
||||||
"#;
|
"#;
|
||||||
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
let default_config = Config::from_kdl(default_config_contents, None).unwrap();
|
||||||
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
let config = Config::from_kdl(config_contents, Some(default_config)).unwrap();
|
||||||
let ctrl_g_normal_mode_action = config
|
let ctrl_g_normal_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&InputMode::Normal,
|
||||||
.get_actions_for_key_in_mode(&InputMode::Normal, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
let ctrl_g_pane_mode_action = config
|
);
|
||||||
.keybinds
|
let ctrl_g_pane_mode_action = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Ctrl('g'));
|
&InputMode::Pane,
|
||||||
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
let r_in_pane_mode = config
|
let r_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('r'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('r')));
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
let t_in_pane_mode = config
|
let t_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('t'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('t')));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctrl_g_normal_mode_action,
|
ctrl_g_normal_mode_action,
|
||||||
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
||||||
|
|
@ -436,9 +445,10 @@ fn can_define_shared_keybinds_for_all_modes() {
|
||||||
"#;
|
"#;
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
for mode in InputMode::iter() {
|
for mode in InputMode::iter() {
|
||||||
let action_in_mode = config
|
let action_in_mode = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&mode,
|
||||||
.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
action_in_mode,
|
action_in_mode,
|
||||||
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
Some(&vec![Action::SwitchToMode(InputMode::Locked)]),
|
||||||
|
|
@ -458,9 +468,10 @@ fn can_define_shared_keybinds_with_exclusion() {
|
||||||
"#;
|
"#;
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
for mode in InputMode::iter() {
|
for mode in InputMode::iter() {
|
||||||
let action_in_mode = config
|
let action_in_mode = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&mode,
|
||||||
.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
if mode == InputMode::Locked {
|
if mode == InputMode::Locked {
|
||||||
assert_eq!(action_in_mode, None, "Keybind unbound in excluded mode");
|
assert_eq!(action_in_mode, None, "Keybind unbound in excluded mode");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -484,9 +495,10 @@ fn can_define_shared_keybinds_with_inclusion() {
|
||||||
"#;
|
"#;
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
for mode in InputMode::iter() {
|
for mode in InputMode::iter() {
|
||||||
let action_in_mode = config
|
let action_in_mode = config.keybinds.get_actions_for_key_in_mode(
|
||||||
.keybinds
|
&mode,
|
||||||
.get_actions_for_key_in_mode(&mode, &Key::Ctrl('g'));
|
&KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier(),
|
||||||
|
);
|
||||||
if mode == InputMode::Normal || mode == InputMode::Resize || mode == InputMode::Pane {
|
if mode == InputMode::Normal || mode == InputMode::Resize || mode == InputMode::Pane {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
action_in_mode,
|
action_in_mode,
|
||||||
|
|
@ -512,7 +524,7 @@ fn keybindings_unbinds_happen_after_binds() {
|
||||||
let config = Config::from_kdl(config_contents, None).unwrap();
|
let config = Config::from_kdl(config_contents, None).unwrap();
|
||||||
let z_in_pane_mode = config
|
let z_in_pane_mode = config
|
||||||
.keybinds
|
.keybinds
|
||||||
.get_actions_for_key_in_mode(&InputMode::Pane, &Key::Char('z'));
|
.get_actions_for_key_in_mode(&InputMode::Pane, &KeyWithModifier::new(BareKey::Char('z')));
|
||||||
assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound");
|
assert_eq!(z_in_pane_mode, None, "Key was ultimately unbound");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
mod kdl_layout_parser;
|
mod kdl_layout_parser;
|
||||||
use crate::data::{
|
use crate::data::{
|
||||||
Direction, FloatingPaneCoordinates, InputMode, Key, LayoutInfo, Palette, PaletteColor,
|
Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, LayoutInfo, Palette,
|
||||||
PaneInfo, PaneManifest, PermissionType, Resize, SessionInfo, TabInfo,
|
PaletteColor, PaneInfo, PaneManifest, PermissionType, Resize, SessionInfo, TabInfo,
|
||||||
};
|
};
|
||||||
use crate::envs::EnvironmentVariables;
|
use crate::envs::EnvironmentVariables;
|
||||||
use crate::home::{find_default_config_dir, get_layout_dir};
|
use crate::home::{find_default_config_dir, get_layout_dir};
|
||||||
|
|
@ -320,7 +320,7 @@ macro_rules! keys_from_kdl {
|
||||||
kdl_string_arguments!($kdl_node)
|
kdl_string_arguments!($kdl_node)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|k| {
|
.map(|k| {
|
||||||
Key::from_str(k).map_err(|_| {
|
KeyWithModifier::from_str(k).map_err(|_| {
|
||||||
ConfigError::new_kdl_error(
|
ConfigError::new_kdl_error(
|
||||||
format!("Invalid key: '{}'", k),
|
format!("Invalid key: '{}'", k),
|
||||||
$kdl_node.span().offset(),
|
$kdl_node.span().offset(),
|
||||||
|
|
@ -388,7 +388,7 @@ impl Action {
|
||||||
action_node: &KdlNode,
|
action_node: &KdlNode,
|
||||||
) -> Result<Self, ConfigError> {
|
) -> Result<Self, ConfigError> {
|
||||||
match action_name {
|
match action_name {
|
||||||
"Write" => Ok(Action::Write(bytes)),
|
"Write" => Ok(Action::Write(None, bytes, false)),
|
||||||
"PaneNameInput" => Ok(Action::PaneNameInput(bytes)),
|
"PaneNameInput" => Ok(Action::PaneNameInput(bytes)),
|
||||||
"TabNameInput" => Ok(Action::TabNameInput(bytes)),
|
"TabNameInput" => Ok(Action::TabNameInput(bytes)),
|
||||||
"SearchInput" => Ok(Action::SearchInput(bytes)),
|
"SearchInput" => Ok(Action::SearchInput(bytes)),
|
||||||
|
|
@ -1614,6 +1614,11 @@ impl Options {
|
||||||
let disable_session_metadata =
|
let disable_session_metadata =
|
||||||
kdl_property_first_arg_as_bool_or_error!(kdl_options, "disable_session_metadata")
|
kdl_property_first_arg_as_bool_or_error!(kdl_options, "disable_session_metadata")
|
||||||
.map(|(v, _)| v);
|
.map(|(v, _)| v);
|
||||||
|
let support_kitty_keyboard_protocol = kdl_property_first_arg_as_bool_or_error!(
|
||||||
|
kdl_options,
|
||||||
|
"support_kitty_keyboard_protocol"
|
||||||
|
)
|
||||||
|
.map(|(v, _)| v);
|
||||||
Ok(Options {
|
Ok(Options {
|
||||||
simplified_ui,
|
simplified_ui,
|
||||||
theme,
|
theme,
|
||||||
|
|
@ -1641,6 +1646,7 @@ impl Options {
|
||||||
styled_underlines,
|
styled_underlines,
|
||||||
serialization_interval,
|
serialization_interval,
|
||||||
disable_session_metadata,
|
disable_session_metadata,
|
||||||
|
support_kitty_keyboard_protocol,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1735,7 +1741,7 @@ impl EnvironmentVariables {
|
||||||
impl Keybinds {
|
impl Keybinds {
|
||||||
fn bind_keys_in_block(
|
fn bind_keys_in_block(
|
||||||
block: &KdlNode,
|
block: &KdlNode,
|
||||||
input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
|
input_mode_keybinds: &mut HashMap<KeyWithModifier, Vec<Action>>,
|
||||||
config_options: &Options,
|
config_options: &Options,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
let all_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode");
|
let all_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode");
|
||||||
|
|
@ -1823,10 +1829,10 @@ impl Keybinds {
|
||||||
}
|
}
|
||||||
fn bind_actions_for_each_key(
|
fn bind_actions_for_each_key(
|
||||||
key_block: &KdlNode,
|
key_block: &KdlNode,
|
||||||
input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
|
input_mode_keybinds: &mut HashMap<KeyWithModifier, Vec<Action>>,
|
||||||
config_options: &Options,
|
config_options: &Options,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
let keys: Vec<Key> = keys_from_kdl!(key_block);
|
let keys: Vec<KeyWithModifier> = keys_from_kdl!(key_block);
|
||||||
let actions: Vec<Action> = actions_from_kdl!(key_block, config_options);
|
let actions: Vec<Action> = actions_from_kdl!(key_block, config_options);
|
||||||
for key in keys {
|
for key in keys {
|
||||||
input_mode_keybinds.insert(key, actions.clone());
|
input_mode_keybinds.insert(key, actions.clone());
|
||||||
|
|
@ -1835,9 +1841,9 @@ impl Keybinds {
|
||||||
}
|
}
|
||||||
fn unbind_keys(
|
fn unbind_keys(
|
||||||
key_block: &KdlNode,
|
key_block: &KdlNode,
|
||||||
input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
|
input_mode_keybinds: &mut HashMap<KeyWithModifier, Vec<Action>>,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
let keys: Vec<Key> = keys_from_kdl!(key_block);
|
let keys: Vec<KeyWithModifier> = keys_from_kdl!(key_block);
|
||||||
for key in keys {
|
for key in keys {
|
||||||
input_mode_keybinds.remove(&key);
|
input_mode_keybinds.remove(&key);
|
||||||
}
|
}
|
||||||
|
|
@ -1847,7 +1853,7 @@ impl Keybinds {
|
||||||
global_unbind: &KdlNode,
|
global_unbind: &KdlNode,
|
||||||
keybinds_from_config: &mut Keybinds,
|
keybinds_from_config: &mut Keybinds,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
let keys: Vec<Key> = keys_from_kdl!(global_unbind);
|
let keys: Vec<KeyWithModifier> = keys_from_kdl!(global_unbind);
|
||||||
for mode in keybinds_from_config.0.values_mut() {
|
for mode in keybinds_from_config.0.values_mut() {
|
||||||
for key in &keys {
|
for key in &keys {
|
||||||
mode.remove(&key);
|
mode.remove(&key);
|
||||||
|
|
@ -1858,7 +1864,7 @@ impl Keybinds {
|
||||||
fn input_mode_keybindings<'a>(
|
fn input_mode_keybindings<'a>(
|
||||||
mode: &KdlNode,
|
mode: &KdlNode,
|
||||||
keybinds_from_config: &'a mut Keybinds,
|
keybinds_from_config: &'a mut Keybinds,
|
||||||
) -> Result<&'a mut HashMap<Key, Vec<Action>>, ConfigError> {
|
) -> Result<&'a mut HashMap<KeyWithModifier, Vec<Action>>, ConfigError> {
|
||||||
let mode_name = kdl_name!(mode);
|
let mode_name = kdl_name!(mode);
|
||||||
let input_mode = InputMode::from_str(mode_name).map_err(|_| {
|
let input_mode = InputMode::from_str(mode_name).map_err(|_| {
|
||||||
ConfigError::new_kdl_error(
|
ConfigError::new_kdl_error(
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ impl TryFrom<ProtobufAction> for Action {
|
||||||
},
|
},
|
||||||
Some(ProtobufActionName::Write) => match protobuf_action.optional_payload {
|
Some(ProtobufActionName::Write) => match protobuf_action.optional_payload {
|
||||||
Some(OptionalPayload::WritePayload(write_payload)) => {
|
Some(OptionalPayload::WritePayload(write_payload)) => {
|
||||||
Ok(Action::Write(write_payload.bytes_to_write))
|
Ok(Action::Write(None, write_payload.bytes_to_write, false))
|
||||||
},
|
},
|
||||||
_ => Err("Wrong payload for Action::Write"),
|
_ => Err("Wrong payload for Action::Write"),
|
||||||
},
|
},
|
||||||
|
|
@ -719,7 +719,7 @@ impl TryFrom<Action> for ProtobufAction {
|
||||||
name: ProtobufActionName::Quit as i32,
|
name: ProtobufActionName::Quit as i32,
|
||||||
optional_payload: None,
|
optional_payload: None,
|
||||||
}),
|
}),
|
||||||
Action::Write(bytes) => Ok(ProtobufAction {
|
Action::Write(_, bytes, _) => Ok(ProtobufAction {
|
||||||
name: ProtobufActionName::Write as i32,
|
name: ProtobufActionName::Write as i32,
|
||||||
optional_payload: Some(OptionalPayload::WritePayload(WritePayload {
|
optional_payload: Some(OptionalPayload::WritePayload(WritePayload {
|
||||||
bytes_to_write: bytes,
|
bytes_to_write: bytes,
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,9 @@ pub use super::generated_api::api::{
|
||||||
};
|
};
|
||||||
#[allow(hidden_glob_reexports)]
|
#[allow(hidden_glob_reexports)]
|
||||||
use crate::data::{
|
use crate::data::{
|
||||||
CopyDestination, Event, EventType, FileMetadata, InputMode, Key, LayoutInfo, ModeInfo, Mouse,
|
CopyDestination, Event, EventType, FileMetadata, InputMode, KeyWithModifier, LayoutInfo,
|
||||||
PaneInfo, PaneManifest, PermissionStatus, PluginCapabilities, SessionInfo, Style, TabInfo,
|
ModeInfo, Mouse, PaneInfo, PaneManifest, PermissionStatus, PluginCapabilities, SessionInfo,
|
||||||
|
Style, TabInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::errors::prelude::*;
|
use crate::errors::prelude::*;
|
||||||
|
|
@ -812,29 +813,30 @@ impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
|
||||||
ProtobufInputMode::from_i32(protobuf_mode_update_payload.current_mode)
|
ProtobufInputMode::from_i32(protobuf_mode_update_payload.current_mode)
|
||||||
.ok_or("Malformed InputMode in the ModeUpdate Event")?
|
.ok_or("Malformed InputMode in the ModeUpdate Event")?
|
||||||
.try_into()?;
|
.try_into()?;
|
||||||
let keybinds: Vec<(InputMode, Vec<(Key, Vec<Action>)>)> = protobuf_mode_update_payload
|
let keybinds: Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)> =
|
||||||
.keybinds
|
protobuf_mode_update_payload
|
||||||
.iter_mut()
|
.keybinds
|
||||||
.filter_map(|k| {
|
.iter_mut()
|
||||||
let input_mode: InputMode = ProtobufInputMode::from_i32(k.mode)
|
.filter_map(|k| {
|
||||||
.ok_or("Malformed InputMode in the ModeUpdate Event")
|
let input_mode: InputMode = ProtobufInputMode::from_i32(k.mode)
|
||||||
.ok()?
|
.ok_or("Malformed InputMode in the ModeUpdate Event")
|
||||||
.try_into()
|
.ok()?
|
||||||
.ok()?;
|
.try_into()
|
||||||
let mut keybinds: Vec<(Key, Vec<Action>)> = vec![];
|
.ok()?;
|
||||||
for mut protobuf_keybind in k.key_bind.drain(..) {
|
let mut keybinds: Vec<(KeyWithModifier, Vec<Action>)> = vec![];
|
||||||
let key: Key = protobuf_keybind.key.unwrap().try_into().ok()?;
|
for mut protobuf_keybind in k.key_bind.drain(..) {
|
||||||
let mut actions: Vec<Action> = vec![];
|
let key: KeyWithModifier = protobuf_keybind.key.unwrap().try_into().ok()?;
|
||||||
for action in protobuf_keybind.action.drain(..) {
|
let mut actions: Vec<Action> = vec![];
|
||||||
if let Ok(action) = action.try_into() {
|
for action in protobuf_keybind.action.drain(..) {
|
||||||
actions.push(action);
|
if let Ok(action) = action.try_into() {
|
||||||
|
actions.push(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
keybinds.push((key, actions));
|
||||||
}
|
}
|
||||||
keybinds.push((key, actions));
|
Some((input_mode, keybinds))
|
||||||
}
|
})
|
||||||
Some((input_mode, keybinds))
|
.collect();
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let style: Style = protobuf_mode_update_payload
|
let style: Style = protobuf_mode_update_payload
|
||||||
.style
|
.style
|
||||||
.and_then(|m| m.try_into().ok())
|
.and_then(|m| m.try_into().ok())
|
||||||
|
|
@ -1047,7 +1049,7 @@ fn serialize_mode_update_event() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_mode_update_event_with_non_default_values() {
|
fn serialize_mode_update_event_with_non_default_values() {
|
||||||
use crate::data::{Direction, Palette, PaletteColor, ThemeHue};
|
use crate::data::{BareKey, Palette, PaletteColor, ThemeHue};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
let mode_update_event = Event::ModeUpdate(ModeInfo {
|
let mode_update_event = Event::ModeUpdate(ModeInfo {
|
||||||
mode: InputMode::Locked,
|
mode: InputMode::Locked,
|
||||||
|
|
@ -1055,14 +1057,14 @@ fn serialize_mode_update_event_with_non_default_values() {
|
||||||
(
|
(
|
||||||
InputMode::Locked,
|
InputMode::Locked,
|
||||||
vec![(
|
vec![(
|
||||||
Key::Alt(crate::data::CharOrArrow::Char('b')),
|
KeyWithModifier::new(BareKey::Char('b')).with_alt_modifier(),
|
||||||
vec![Action::SwitchToMode(InputMode::Normal)],
|
vec![Action::SwitchToMode(InputMode::Normal)],
|
||||||
)],
|
)],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
InputMode::Tab,
|
InputMode::Tab,
|
||||||
vec![(
|
vec![(
|
||||||
Key::Alt(crate::data::CharOrArrow::Direction(Direction::Up)),
|
KeyWithModifier::new(BareKey::Up).with_alt_modifier(),
|
||||||
vec![Action::SwitchToMode(InputMode::Pane)],
|
vec![Action::SwitchToMode(InputMode::Pane)],
|
||||||
)],
|
)],
|
||||||
),
|
),
|
||||||
|
|
@ -1070,13 +1072,16 @@ fn serialize_mode_update_event_with_non_default_values() {
|
||||||
InputMode::Pane,
|
InputMode::Pane,
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Key::Ctrl('b'),
|
KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
|
||||||
vec![
|
vec![
|
||||||
Action::SwitchToMode(InputMode::Tmux),
|
Action::SwitchToMode(InputMode::Tmux),
|
||||||
Action::Write(vec![10]),
|
Action::Write(None, vec![10], false),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
(Key::Char('a'), vec![Action::WriteChars("foo".to_owned())]),
|
(
|
||||||
|
KeyWithModifier::new(BareKey::Char('a')),
|
||||||
|
vec![Action::WriteChars("foo".to_owned())],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -1192,8 +1197,9 @@ fn serialize_pane_update_event() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_key_event() {
|
fn serialize_key_event() {
|
||||||
|
use crate::data::BareKey;
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
let key_event = Event::Key(Key::Ctrl('a'));
|
let key_event = Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier());
|
||||||
let protobuf_event: ProtobufEvent = key_event.clone().try_into().unwrap();
|
let protobuf_event: ProtobufEvent = key_event.clone().try_into().unwrap();
|
||||||
let serialized_protobuf_event = protobuf_event.encode_to_vec();
|
let serialized_protobuf_event = protobuf_event.encode_to_vec();
|
||||||
let deserialized_protobuf_event: ProtobufEvent =
|
let deserialized_protobuf_event: ProtobufEvent =
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ message Key {
|
||||||
enum KeyModifier {
|
enum KeyModifier {
|
||||||
CTRL = 0;
|
CTRL = 0;
|
||||||
ALT = 1;
|
ALT = 1;
|
||||||
|
SHIFT = 2;
|
||||||
|
SUPER = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NamedKey {
|
enum NamedKey {
|
||||||
|
|
@ -34,6 +36,13 @@ message Key {
|
||||||
F12 = 22;
|
F12 = 22;
|
||||||
Tab = 23;
|
Tab = 23;
|
||||||
Esc = 24;
|
Esc = 24;
|
||||||
|
CapsLock = 25;
|
||||||
|
ScrollLock = 26;
|
||||||
|
NumLock = 27;
|
||||||
|
PrintScreen = 28;
|
||||||
|
Pause = 29;
|
||||||
|
Menu = 30;
|
||||||
|
Enter = 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Char {
|
enum Char {
|
||||||
|
|
@ -80,4 +89,5 @@ message Key {
|
||||||
NamedKey key = 2;
|
NamedKey key = 2;
|
||||||
Char char = 3;
|
Char char = 3;
|
||||||
}
|
}
|
||||||
|
repeated KeyModifier additional_modifiers = 4;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,258 +1,184 @@
|
||||||
pub use super::generated_api::api::key::{
|
pub use super::generated_api::api::key::{
|
||||||
key::{KeyModifier, MainKey, NamedKey},
|
key::{
|
||||||
|
KeyModifier as ProtobufKeyModifier, MainKey as ProtobufMainKey,
|
||||||
|
NamedKey as ProtobufNamedKey,
|
||||||
|
},
|
||||||
Key as ProtobufKey,
|
Key as ProtobufKey,
|
||||||
};
|
};
|
||||||
use crate::data::{CharOrArrow, Direction, Key};
|
use crate::data::{BareKey, KeyModifier, KeyWithModifier};
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
impl TryFrom<ProtobufKey> for Key {
|
impl TryFrom<ProtobufMainKey> for BareKey {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(protobuf_main_key: ProtobufMainKey) -> Result<Self, &'static str> {
|
||||||
|
match protobuf_main_key {
|
||||||
|
ProtobufMainKey::Char(character) => Ok(BareKey::Char(char_index_to_char(character))),
|
||||||
|
ProtobufMainKey::Key(key_index) => {
|
||||||
|
let key = ProtobufNamedKey::from_i32(key_index).ok_or("invalid_key")?;
|
||||||
|
Ok(named_key_to_bare_key(key))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<BareKey> for ProtobufMainKey {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(bare_key: BareKey) -> Result<Self, &'static str> {
|
||||||
|
match bare_key {
|
||||||
|
BareKey::PageDown => Ok(ProtobufMainKey::Key(ProtobufNamedKey::PageDown as i32)),
|
||||||
|
BareKey::PageUp => Ok(ProtobufMainKey::Key(ProtobufNamedKey::PageUp as i32)),
|
||||||
|
BareKey::Left => Ok(ProtobufMainKey::Key(ProtobufNamedKey::LeftArrow as i32)),
|
||||||
|
BareKey::Down => Ok(ProtobufMainKey::Key(ProtobufNamedKey::DownArrow as i32)),
|
||||||
|
BareKey::Up => Ok(ProtobufMainKey::Key(ProtobufNamedKey::UpArrow as i32)),
|
||||||
|
BareKey::Right => Ok(ProtobufMainKey::Key(ProtobufNamedKey::RightArrow as i32)),
|
||||||
|
BareKey::Home => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Home as i32)),
|
||||||
|
BareKey::End => Ok(ProtobufMainKey::Key(ProtobufNamedKey::End as i32)),
|
||||||
|
BareKey::Backspace => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Backspace as i32)),
|
||||||
|
BareKey::Delete => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Delete as i32)),
|
||||||
|
BareKey::Insert => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Insert as i32)),
|
||||||
|
BareKey::F(f_index) => fn_index_to_main_key(f_index),
|
||||||
|
BareKey::Char(character) => Ok(ProtobufMainKey::Char(character as i32)),
|
||||||
|
BareKey::Tab => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Tab as i32)),
|
||||||
|
BareKey::Esc => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Esc as i32)),
|
||||||
|
BareKey::Enter => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Enter as i32)),
|
||||||
|
BareKey::CapsLock => Ok(ProtobufMainKey::Key(ProtobufNamedKey::CapsLock as i32)),
|
||||||
|
BareKey::ScrollLock => Ok(ProtobufMainKey::Key(ProtobufNamedKey::ScrollLock as i32)),
|
||||||
|
BareKey::NumLock => Ok(ProtobufMainKey::Key(ProtobufNamedKey::NumLock as i32)),
|
||||||
|
BareKey::PrintScreen => Ok(ProtobufMainKey::Key(ProtobufNamedKey::PrintScreen as i32)),
|
||||||
|
BareKey::Pause => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Pause as i32)),
|
||||||
|
BareKey::Menu => Ok(ProtobufMainKey::Key(ProtobufNamedKey::Menu as i32)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtobufKeyModifier> for KeyModifier {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(protobuf_key_modifier: ProtobufKeyModifier) -> Result<Self, &'static str> {
|
||||||
|
match protobuf_key_modifier {
|
||||||
|
ProtobufKeyModifier::Ctrl => Ok(KeyModifier::Ctrl),
|
||||||
|
ProtobufKeyModifier::Alt => Ok(KeyModifier::Alt),
|
||||||
|
ProtobufKeyModifier::Shift => Ok(KeyModifier::Shift),
|
||||||
|
ProtobufKeyModifier::Super => Ok(KeyModifier::Super),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<KeyModifier> for ProtobufKeyModifier {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(key_modifier: KeyModifier) -> Result<Self, &'static str> {
|
||||||
|
match key_modifier {
|
||||||
|
KeyModifier::Ctrl => Ok(ProtobufKeyModifier::Ctrl),
|
||||||
|
KeyModifier::Alt => Ok(ProtobufKeyModifier::Alt),
|
||||||
|
KeyModifier::Shift => Ok(ProtobufKeyModifier::Shift),
|
||||||
|
KeyModifier::Super => Ok(ProtobufKeyModifier::Super),
|
||||||
|
_ => Err("unsupported key modifier"), // TODO: test this so we don't crash if we have a
|
||||||
|
// Capslock or something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtobufKey> for KeyWithModifier {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(protobuf_key: ProtobufKey) -> Result<Self, &'static str> {
|
fn try_from(protobuf_key: ProtobufKey) -> Result<Self, &'static str> {
|
||||||
let key_modifier = parse_optional_modifier(&protobuf_key);
|
let bare_key = protobuf_key
|
||||||
match key_modifier {
|
.main_key
|
||||||
Some(KeyModifier::Ctrl) => {
|
.ok_or("Key must have main_key")?
|
||||||
if let Ok(character) = char_from_main_key(protobuf_key.main_key.clone()) {
|
.try_into()?;
|
||||||
Ok(Key::Ctrl(character))
|
let mut key_modifiers = BTreeSet::new();
|
||||||
} else {
|
if let Some(main_modifier) = protobuf_key.modifier {
|
||||||
let index = fn_index_from_main_key(protobuf_key.main_key)?;
|
key_modifiers.insert(
|
||||||
Ok(Key::CtrlF(index))
|
ProtobufKeyModifier::from_i32(main_modifier)
|
||||||
}
|
.ok_or("invalid key modifier")?
|
||||||
},
|
.try_into()?,
|
||||||
Some(KeyModifier::Alt) => {
|
);
|
||||||
if let Ok(char_or_arrow) = CharOrArrow::from_main_key(protobuf_key.main_key.clone())
|
|
||||||
{
|
|
||||||
Ok(Key::Alt(char_or_arrow))
|
|
||||||
} else {
|
|
||||||
let index = fn_index_from_main_key(protobuf_key.main_key)?;
|
|
||||||
Ok(Key::AltF(index))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => match protobuf_key.main_key.as_ref().ok_or("invalid key")? {
|
|
||||||
MainKey::Char(_key_index) => {
|
|
||||||
let character = char_from_main_key(protobuf_key.main_key)?;
|
|
||||||
Ok(Key::Char(character))
|
|
||||||
},
|
|
||||||
MainKey::Key(key_index) => {
|
|
||||||
let key = NamedKey::from_i32(*key_index).ok_or("invalid_key")?;
|
|
||||||
Ok(named_key_to_key(key))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
for key_modifier in protobuf_key.additional_modifiers {
|
||||||
|
key_modifiers.insert(
|
||||||
|
ProtobufKeyModifier::from_i32(key_modifier)
|
||||||
|
.ok_or("invalid key modifier")?
|
||||||
|
.try_into()?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(KeyWithModifier {
|
||||||
|
bare_key,
|
||||||
|
key_modifiers,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Key> for ProtobufKey {
|
impl TryFrom<KeyWithModifier> for ProtobufKey {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(key: Key) -> Result<Self, &'static str> {
|
fn try_from(key_with_modifier: KeyWithModifier) -> Result<Self, &'static str> {
|
||||||
match key {
|
let mut modifiers: Vec<ProtobufKeyModifier> = vec![];
|
||||||
Key::PageDown => Ok(ProtobufKey {
|
for key_modifier in key_with_modifier.key_modifiers {
|
||||||
modifier: None,
|
modifiers.push(key_modifier.try_into()?);
|
||||||
main_key: Some(MainKey::Key(NamedKey::PageDown as i32)),
|
|
||||||
}),
|
|
||||||
Key::PageUp => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::PageUp as i32)),
|
|
||||||
}),
|
|
||||||
Key::Left => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::LeftArrow as i32)),
|
|
||||||
}),
|
|
||||||
Key::Down => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::DownArrow as i32)),
|
|
||||||
}),
|
|
||||||
Key::Up => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::UpArrow as i32)),
|
|
||||||
}),
|
|
||||||
Key::Right => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::RightArrow as i32)),
|
|
||||||
}),
|
|
||||||
Key::Home => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::Home as i32)),
|
|
||||||
}),
|
|
||||||
Key::End => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::End as i32)),
|
|
||||||
}),
|
|
||||||
Key::Backspace => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::Backspace as i32)),
|
|
||||||
}),
|
|
||||||
Key::Delete => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::Delete as i32)),
|
|
||||||
}),
|
|
||||||
Key::Insert => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::Insert as i32)),
|
|
||||||
}),
|
|
||||||
Key::F(index) => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(fn_index_to_main_key(index)?),
|
|
||||||
}),
|
|
||||||
Key::CtrlF(index) => Ok(ProtobufKey {
|
|
||||||
modifier: Some(KeyModifier::Ctrl as i32),
|
|
||||||
main_key: Some(fn_index_to_main_key(index)?),
|
|
||||||
}),
|
|
||||||
Key::AltF(index) => Ok(ProtobufKey {
|
|
||||||
modifier: Some(KeyModifier::Alt as i32),
|
|
||||||
main_key: Some(fn_index_to_main_key(index)?),
|
|
||||||
}),
|
|
||||||
Key::Char(character) => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Char((character as u8) as i32)),
|
|
||||||
}),
|
|
||||||
Key::Alt(char_or_arrow) => {
|
|
||||||
let main_key = match char_or_arrow {
|
|
||||||
CharOrArrow::Char(character) => MainKey::Char((character as u8) as i32),
|
|
||||||
CharOrArrow::Direction(Direction::Left) => {
|
|
||||||
MainKey::Key(NamedKey::LeftArrow as i32)
|
|
||||||
},
|
|
||||||
CharOrArrow::Direction(Direction::Right) => {
|
|
||||||
MainKey::Key(NamedKey::RightArrow as i32)
|
|
||||||
},
|
|
||||||
CharOrArrow::Direction(Direction::Up) => MainKey::Key(NamedKey::UpArrow as i32),
|
|
||||||
CharOrArrow::Direction(Direction::Down) => {
|
|
||||||
MainKey::Key(NamedKey::DownArrow as i32)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Ok(ProtobufKey {
|
|
||||||
modifier: Some(KeyModifier::Alt as i32),
|
|
||||||
main_key: Some(main_key),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
Key::Ctrl(character) => Ok(ProtobufKey {
|
|
||||||
modifier: Some(KeyModifier::Ctrl as i32),
|
|
||||||
main_key: Some(MainKey::Char((character as u8) as i32)),
|
|
||||||
}),
|
|
||||||
Key::BackTab => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::Tab as i32)),
|
|
||||||
}),
|
|
||||||
Key::Null => {
|
|
||||||
Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: None, // TODO: does this break deserialization?
|
|
||||||
})
|
|
||||||
},
|
|
||||||
Key::Esc => Ok(ProtobufKey {
|
|
||||||
modifier: None,
|
|
||||||
main_key: Some(MainKey::Key(NamedKey::Esc as i32)),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(ProtobufKey {
|
||||||
|
main_key: Some(key_with_modifier.bare_key.try_into()?),
|
||||||
|
modifier: modifiers.pop().map(|m| m as i32),
|
||||||
|
additional_modifiers: modifiers.into_iter().map(|m| m as i32).collect(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_index_to_main_key(index: u8) -> Result<MainKey, &'static str> {
|
fn fn_index_to_main_key(index: u8) -> Result<ProtobufMainKey, &'static str> {
|
||||||
match index {
|
match index {
|
||||||
1 => Ok(MainKey::Key(NamedKey::F1 as i32)),
|
1 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F1 as i32)),
|
||||||
2 => Ok(MainKey::Key(NamedKey::F2 as i32)),
|
2 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F2 as i32)),
|
||||||
3 => Ok(MainKey::Key(NamedKey::F3 as i32)),
|
3 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F3 as i32)),
|
||||||
4 => Ok(MainKey::Key(NamedKey::F4 as i32)),
|
4 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F4 as i32)),
|
||||||
5 => Ok(MainKey::Key(NamedKey::F5 as i32)),
|
5 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F5 as i32)),
|
||||||
6 => Ok(MainKey::Key(NamedKey::F6 as i32)),
|
6 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F6 as i32)),
|
||||||
7 => Ok(MainKey::Key(NamedKey::F7 as i32)),
|
7 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F7 as i32)),
|
||||||
8 => Ok(MainKey::Key(NamedKey::F8 as i32)),
|
8 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F8 as i32)),
|
||||||
9 => Ok(MainKey::Key(NamedKey::F9 as i32)),
|
9 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F9 as i32)),
|
||||||
10 => Ok(MainKey::Key(NamedKey::F10 as i32)),
|
10 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F10 as i32)),
|
||||||
11 => Ok(MainKey::Key(NamedKey::F11 as i32)),
|
11 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F11 as i32)),
|
||||||
12 => Ok(MainKey::Key(NamedKey::F12 as i32)),
|
12 => Ok(ProtobufMainKey::Key(ProtobufNamedKey::F12 as i32)),
|
||||||
_ => Err("Invalid key"),
|
_ => Err("Invalid key"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharOrArrow {
|
|
||||||
pub fn from_main_key(
|
|
||||||
main_key: std::option::Option<MainKey>,
|
|
||||||
) -> Result<CharOrArrow, &'static str> {
|
|
||||||
match main_key {
|
|
||||||
Some(MainKey::Char(encoded_key)) => {
|
|
||||||
Ok(CharOrArrow::Char(char_index_to_char(encoded_key)))
|
|
||||||
},
|
|
||||||
Some(MainKey::Key(key_index)) => match NamedKey::from_i32(key_index) {
|
|
||||||
Some(NamedKey::LeftArrow) => Ok(CharOrArrow::Direction(Direction::Left)),
|
|
||||||
Some(NamedKey::RightArrow) => Ok(CharOrArrow::Direction(Direction::Right)),
|
|
||||||
Some(NamedKey::UpArrow) => Ok(CharOrArrow::Direction(Direction::Up)),
|
|
||||||
Some(NamedKey::DownArrow) => Ok(CharOrArrow::Direction(Direction::Down)),
|
|
||||||
_ => Err("Unsupported key"),
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err("Unsupported key");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_optional_modifier(m: &ProtobufKey) -> Option<KeyModifier> {
|
|
||||||
match m.modifier {
|
|
||||||
Some(modifier) => KeyModifier::from_i32(modifier),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn char_index_to_char(char_index: i32) -> char {
|
fn char_index_to_char(char_index: i32) -> char {
|
||||||
char_index as u8 as char
|
char_index as u8 as char
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char_from_main_key(main_key: Option<MainKey>) -> Result<char, &'static str> {
|
fn named_key_to_bare_key(named_key: ProtobufNamedKey) -> BareKey {
|
||||||
match main_key {
|
|
||||||
Some(MainKey::Char(encoded_key)) => {
|
|
||||||
return Ok(char_index_to_char(encoded_key));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err("Unsupported key");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_index_from_main_key(main_key: Option<MainKey>) -> Result<u8, &'static str> {
|
|
||||||
match main_key {
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F1 as i32 => Ok(1),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F2 as i32 => Ok(2),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F3 as i32 => Ok(3),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F4 as i32 => Ok(4),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F5 as i32 => Ok(5),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F6 as i32 => Ok(6),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F7 as i32 => Ok(7),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F8 as i32 => Ok(8),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F9 as i32 => Ok(9),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F10 as i32 => Ok(10),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F11 as i32 => Ok(11),
|
|
||||||
Some(MainKey::Key(n)) if n == NamedKey::F12 as i32 => Ok(12),
|
|
||||||
_ => Err("Unsupported key"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn named_key_to_key(named_key: NamedKey) -> Key {
|
|
||||||
match named_key {
|
match named_key {
|
||||||
NamedKey::PageDown => Key::PageDown,
|
ProtobufNamedKey::PageDown => BareKey::PageDown,
|
||||||
NamedKey::PageUp => Key::PageUp,
|
ProtobufNamedKey::PageUp => BareKey::PageUp,
|
||||||
NamedKey::LeftArrow => Key::Left,
|
ProtobufNamedKey::LeftArrow => BareKey::Left,
|
||||||
NamedKey::DownArrow => Key::Down,
|
ProtobufNamedKey::DownArrow => BareKey::Down,
|
||||||
NamedKey::UpArrow => Key::Up,
|
ProtobufNamedKey::UpArrow => BareKey::Up,
|
||||||
NamedKey::RightArrow => Key::Right,
|
ProtobufNamedKey::RightArrow => BareKey::Right,
|
||||||
NamedKey::Home => Key::Home,
|
ProtobufNamedKey::Home => BareKey::Home,
|
||||||
NamedKey::End => Key::End,
|
ProtobufNamedKey::End => BareKey::End,
|
||||||
NamedKey::Backspace => Key::Backspace,
|
ProtobufNamedKey::Backspace => BareKey::Backspace,
|
||||||
NamedKey::Delete => Key::Delete,
|
ProtobufNamedKey::Delete => BareKey::Delete,
|
||||||
NamedKey::Insert => Key::Insert,
|
ProtobufNamedKey::Insert => BareKey::Insert,
|
||||||
NamedKey::F1 => Key::F(1),
|
ProtobufNamedKey::F1 => BareKey::F(1),
|
||||||
NamedKey::F2 => Key::F(2),
|
ProtobufNamedKey::F2 => BareKey::F(2),
|
||||||
NamedKey::F3 => Key::F(3),
|
ProtobufNamedKey::F3 => BareKey::F(3),
|
||||||
NamedKey::F4 => Key::F(4),
|
ProtobufNamedKey::F4 => BareKey::F(4),
|
||||||
NamedKey::F5 => Key::F(5),
|
ProtobufNamedKey::F5 => BareKey::F(5),
|
||||||
NamedKey::F6 => Key::F(6),
|
ProtobufNamedKey::F6 => BareKey::F(6),
|
||||||
NamedKey::F7 => Key::F(7),
|
ProtobufNamedKey::F7 => BareKey::F(7),
|
||||||
NamedKey::F8 => Key::F(8),
|
ProtobufNamedKey::F8 => BareKey::F(8),
|
||||||
NamedKey::F9 => Key::F(9),
|
ProtobufNamedKey::F9 => BareKey::F(9),
|
||||||
NamedKey::F10 => Key::F(10),
|
ProtobufNamedKey::F10 => BareKey::F(10),
|
||||||
NamedKey::F11 => Key::F(11),
|
ProtobufNamedKey::F11 => BareKey::F(11),
|
||||||
NamedKey::F12 => Key::F(12),
|
ProtobufNamedKey::F12 => BareKey::F(12),
|
||||||
NamedKey::Tab => Key::BackTab,
|
ProtobufNamedKey::Tab => BareKey::Tab,
|
||||||
NamedKey::Esc => Key::Esc,
|
ProtobufNamedKey::Esc => BareKey::Esc,
|
||||||
|
ProtobufNamedKey::CapsLock => BareKey::CapsLock,
|
||||||
|
ProtobufNamedKey::ScrollLock => BareKey::ScrollLock,
|
||||||
|
ProtobufNamedKey::PrintScreen => BareKey::PrintScreen,
|
||||||
|
ProtobufNamedKey::Pause => BareKey::Pause,
|
||||||
|
ProtobufNamedKey::Menu => BareKey::Menu,
|
||||||
|
ProtobufNamedKey::NumLock => BareKey::NumLock,
|
||||||
|
ProtobufNamedKey::Enter => BareKey::Enter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 713
|
assertion_line: 740
|
||||||
expression: "format!(\"{:#?}\", options)"
|
expression: "format!(\"{:#?}\", options)"
|
||||||
---
|
---
|
||||||
Options {
|
Options {
|
||||||
|
|
@ -32,4 +32,5 @@ Options {
|
||||||
styled_underlines: None,
|
styled_underlines: None,
|
||||||
serialization_interval: None,
|
serialization_interval: None,
|
||||||
disable_session_metadata: None,
|
disable_session_metadata: None,
|
||||||
|
support_kitty_keyboard_protocol: None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 741
|
assertion_line: 768
|
||||||
expression: "format!(\"{:#?}\", options)"
|
expression: "format!(\"{:#?}\", options)"
|
||||||
---
|
---
|
||||||
Options {
|
Options {
|
||||||
|
|
@ -32,4 +32,5 @@ Options {
|
||||||
styled_underlines: None,
|
styled_underlines: None,
|
||||||
serialization_interval: None,
|
serialization_interval: None,
|
||||||
disable_session_metadata: None,
|
disable_session_metadata: None,
|
||||||
|
support_kitty_keyboard_protocol: None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 700
|
assertion_line: 727
|
||||||
expression: "format!(\"{:#?}\", options)"
|
expression: "format!(\"{:#?}\", options)"
|
||||||
---
|
---
|
||||||
Options {
|
Options {
|
||||||
|
|
@ -30,4 +30,5 @@ Options {
|
||||||
styled_underlines: None,
|
styled_underlines: None,
|
||||||
serialization_interval: None,
|
serialization_interval: None,
|
||||||
disable_session_metadata: None,
|
disable_session_metadata: None,
|
||||||
|
support_kitty_keyboard_protocol: None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,60 +1,89 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 798
|
assertion_line: 825
|
||||||
expression: "format!(\"{:#?}\", config)"
|
expression: "format!(\"{:#?}\", config)"
|
||||||
---
|
---
|
||||||
Config {
|
Config {
|
||||||
keybinds: {
|
keybinds: {
|
||||||
Normal: {
|
Normal: {
|
||||||
Char(
|
KeyWithModifier {
|
||||||
'b',
|
bare_key: Char(
|
||||||
): [
|
'b',
|
||||||
|
),
|
||||||
|
key_modifiers: {},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Session,
|
Session,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Ctrl(
|
KeyWithModifier {
|
||||||
'b',
|
bare_key: Char(
|
||||||
): [
|
'b',
|
||||||
|
),
|
||||||
|
key_modifiers: {
|
||||||
|
Ctrl,
|
||||||
|
},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Resize,
|
Resize,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Ctrl(
|
KeyWithModifier {
|
||||||
'c',
|
bare_key: Char(
|
||||||
): [
|
'c',
|
||||||
|
),
|
||||||
|
key_modifiers: {
|
||||||
|
Ctrl,
|
||||||
|
},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Resize,
|
Resize,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
Resize: {
|
Resize: {
|
||||||
Char(
|
KeyWithModifier {
|
||||||
'b',
|
bare_key: Char(
|
||||||
): [
|
'b',
|
||||||
|
),
|
||||||
|
key_modifiers: {},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Locked,
|
Locked,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Ctrl(
|
KeyWithModifier {
|
||||||
'c',
|
bare_key: Char(
|
||||||
): [
|
'c',
|
||||||
|
),
|
||||||
|
key_modifiers: {
|
||||||
|
Ctrl,
|
||||||
|
},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Resize,
|
Resize,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
Scroll: {
|
Scroll: {
|
||||||
Char(
|
KeyWithModifier {
|
||||||
'b',
|
bare_key: Char(
|
||||||
): [
|
'b',
|
||||||
|
),
|
||||||
|
key_modifiers: {},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Locked,
|
Locked,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Ctrl(
|
KeyWithModifier {
|
||||||
'c',
|
bare_key: Char(
|
||||||
): [
|
'c',
|
||||||
|
),
|
||||||
|
key_modifiers: {
|
||||||
|
Ctrl,
|
||||||
|
},
|
||||||
|
}: [
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
Resize,
|
Resize,
|
||||||
),
|
),
|
||||||
|
|
@ -88,6 +117,7 @@ Config {
|
||||||
styled_underlines: None,
|
styled_underlines: None,
|
||||||
serialization_interval: None,
|
serialization_interval: None,
|
||||||
disable_session_metadata: None,
|
disable_session_metadata: None,
|
||||||
|
support_kitty_keyboard_protocol: None,
|
||||||
},
|
},
|
||||||
themes: {},
|
themes: {},
|
||||||
plugins: PluginAliases {
|
plugins: PluginAliases {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 723
|
assertion_line: 750
|
||||||
expression: "format!(\"{:#?}\", options)"
|
expression: "format!(\"{:#?}\", options)"
|
||||||
---
|
---
|
||||||
Options {
|
Options {
|
||||||
|
|
@ -32,4 +32,5 @@ Options {
|
||||||
styled_underlines: None,
|
styled_underlines: None,
|
||||||
serialization_interval: None,
|
serialization_interval: None,
|
||||||
disable_session_metadata: None,
|
disable_session_metadata: None,
|
||||||
|
support_kitty_keyboard_protocol: None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue