use crossterm::event::{KeyCode, KeyModifiers};
use serde::{Deserialize, Serialize, de};
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct KeyBinding(pub KeyCode, pub KeyModifiers);
impl KeyBinding {
pub fn matches(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
if self.0 != key {
return false;
}
let has_ctrl = self.1.contains(KeyModifiers::CONTROL);
let has_alt = self.1.contains(KeyModifiers::ALT);
let has_shift = self.1.contains(KeyModifiers::SHIFT);
if !has_ctrl && !has_alt {
return match self.0 {
KeyCode::Char(_) => {
!modifiers.contains(KeyModifiers::CONTROL)
&& !modifiers.contains(KeyModifiers::ALT)
}
KeyCode::BackTab => {
!modifiers.contains(KeyModifiers::CONTROL)
&& !modifiers.contains(KeyModifiers::ALT)
}
_ => {
let shift_ok = has_shift == modifiers.contains(KeyModifiers::SHIFT);
shift_ok
&& !modifiers.contains(KeyModifiers::CONTROL)
&& !modifiers.contains(KeyModifiers::ALT)
}
};
}
let ctrl_ok = !has_ctrl || modifiers.contains(KeyModifiers::CONTROL);
let alt_ok = !has_alt || modifiers.contains(KeyModifiers::ALT);
ctrl_ok && alt_ok
}
pub(crate) fn parse(s: &str) -> Result<Self, String> {
if s.eq_ignore_ascii_case("shift+tab") {
return Ok(KeyBinding(KeyCode::BackTab, KeyModifiers::NONE));
}
let mut mods = KeyModifiers::NONE;
let mut rest = s;
loop {
if let Some(r) = rest
.strip_prefix("Ctrl+")
.or_else(|| rest.strip_prefix("ctrl+"))
{
mods |= KeyModifiers::CONTROL;
rest = r;
} else if let Some(r) = rest
.strip_prefix("Alt+")
.or_else(|| rest.strip_prefix("alt+"))
{
mods |= KeyModifiers::ALT;
rest = r;
} else if let Some(r) = rest
.strip_prefix("Shift+")
.or_else(|| rest.strip_prefix("shift+"))
{
mods |= KeyModifiers::SHIFT;
rest = r;
} else {
break;
}
}
let key = match rest {
"Tab" | "tab" => KeyCode::Tab,
"PageDown" | "pagedown" => KeyCode::PageDown,
"PageUp" | "pageup" => KeyCode::PageUp,
"Space" | "space" => KeyCode::Char(' '),
"Esc" | "esc" => KeyCode::Esc,
"Up" | "up" => KeyCode::Up,
"Down" | "down" => KeyCode::Down,
"Left" | "left" => KeyCode::Left,
"Right" | "right" => KeyCode::Right,
"Enter" | "enter" => KeyCode::Enter,
"Backspace" | "backspace" => KeyCode::Backspace,
"Delete" | "delete" => KeyCode::Delete,
"Home" | "home" => KeyCode::Home,
"End" | "end" => KeyCode::End,
"Insert" | "insert" => KeyCode::Insert,
s if s.chars().count() == 1 => KeyCode::Char(s.chars().next().unwrap()),
s if s.starts_with('F') || s.starts_with('f') => {
let n: u8 = s[1..]
.parse()
.map_err(|_| format!("Unknown key: {:?}", s))?;
KeyCode::F(n)
}
other => return Err(format!("Unknown key: {:?}", other)),
};
Ok(KeyBinding(key, mods))
}
}
impl KeyBinding {
pub fn display(&self) -> String {
if self.0 == KeyCode::BackTab {
return "Shift+Tab".to_string();
}
let mut s = String::new();
if self.1.contains(KeyModifiers::CONTROL) {
s.push_str("Ctrl+");
}
if self.1.contains(KeyModifiers::ALT) {
s.push_str("Alt+");
}
if self.1.contains(KeyModifiers::SHIFT) {
s.push_str("Shift+");
}
match self.0 {
KeyCode::Tab => s.push_str("Tab"),
KeyCode::PageDown => s.push_str("PageDown"),
KeyCode::PageUp => s.push_str("PageUp"),
KeyCode::Char(' ') => s.push_str("Space"),
KeyCode::Esc => s.push_str("Esc"),
KeyCode::Up => s.push_str("Up"),
KeyCode::Down => s.push_str("Down"),
KeyCode::Left => s.push_str("Left"),
KeyCode::Right => s.push_str("Right"),
KeyCode::Enter => s.push_str("Enter"),
KeyCode::Backspace => s.push_str("Backspace"),
KeyCode::Delete => s.push_str("Delete"),
KeyCode::Home => s.push_str("Home"),
KeyCode::End => s.push_str("End"),
KeyCode::Insert => s.push_str("Insert"),
KeyCode::Char(c) => s.push(c),
KeyCode::F(n) => s.push_str(&format!("F{}", n)),
_ => s.push('?'),
}
s
}
}
impl Serialize for KeyBinding {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.display())
}
}
impl<'de> Deserialize<'de> for KeyBinding {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
KeyBinding::parse(&s).map_err(de::Error::custom)
}
}
impl schemars::JsonSchema for KeyBinding {
fn schema_name() -> String {
"KeyBinding".to_string()
}
fn json_schema(_g: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
instance_type: Some(schemars::schema::InstanceType::String.into()),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some(
"A key combination string, e.g. \"Ctrl+d\", \"Shift+Tab\", \"F1\", \"j\""
.into(),
),
examples: vec![
serde_json::json!("j"),
serde_json::json!("Ctrl+d"),
serde_json::json!("Shift+Tab"),
serde_json::json!("F1"),
],
..Default::default()
})),
..Default::default()
}
.into()
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct KeyBindings(pub Vec<KeyBinding>);
impl KeyBindings {
pub fn matches(&self, key: KeyCode, mods: KeyModifiers) -> bool {
self.0.iter().any(|b| b.matches(key, mods))
}
pub fn display(&self) -> String {
self.0
.iter()
.map(|b| b.display())
.collect::<Vec<_>>()
.join("/")
}
pub fn has_overlap(&self, other: &KeyBindings) -> bool {
for a in &self.0 {
for b in &other.0 {
if a.0 == b.0 && a.1 == b.1 {
return true;
}
}
}
false
}
}
impl Serialize for KeyBindings {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self.0.as_slice() {
[single] => single.serialize(serializer),
many => many.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for KeyBindings {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = KeyBindings;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a key string or array of key strings")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<KeyBindings, E> {
KeyBinding::parse(v)
.map(|b| KeyBindings(vec![b]))
.map_err(E::custom)
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<KeyBindings, A::Error> {
let mut bindings = Vec::new();
while let Some(s) = seq.next_element::<String>()? {
let b = KeyBinding::parse(&s).map_err(de::Error::custom)?;
bindings.push(b);
}
Ok(KeyBindings(bindings))
}
}
deserializer.deserialize_any(Visitor)
}
}
impl schemars::JsonSchema for KeyBindings {
fn schema_name() -> String {
"KeyBindings".to_string()
}
fn json_schema(g: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
let str_schema = g.subschema_for::<String>();
let arr_schema = g.subschema_for::<Vec<String>>();
schemars::schema::SchemaObject {
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
one_of: Some(vec![str_schema, arr_schema]),
..Default::default()
})),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some(
"One key binding string or an array of alternatives, e.g. \"j\" or [\"j\", \"Down\"]".into(),
),
..Default::default()
})),
..Default::default()
}
.into()
}
}
#[inline(always)]
fn default_scroll_down() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('j'), KeyModifiers::NONE),
KeyBinding(KeyCode::Down, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_scroll_up() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('k'), KeyModifiers::NONE),
KeyBinding(KeyCode::Up, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_scroll_left() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('h'), KeyModifiers::NONE),
KeyBinding(KeyCode::Left, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_scroll_right() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('l'), KeyModifiers::NONE),
KeyBinding(KeyCode::Right, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_start_of_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('0'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_end_of_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('$'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_half_page_down() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('d'), KeyModifiers::CONTROL)])
}
#[inline(always)]
fn default_half_page_up() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('u'), KeyModifiers::CONTROL)])
}
#[inline(always)]
fn default_page_down() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::PageDown, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_page_up() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::PageUp, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_command_mode() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char(':'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_mode_key() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('f'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_toggle_filtering() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('F'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_toggle_sidebar() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('s'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_toggle_mode_bar() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('b'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_toggle_borders() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('B'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_go_to_top_chord() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('g'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_go_to_bottom() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('G'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_mark_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('m'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_search_forward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('/'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_search_backward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('?'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_next_match() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('n'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_prev_match() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('N'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_toggle_wrap() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('w'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_visual_mode() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('V'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_visual_char() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('v'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_toggle_marks_only() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('M'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_yank_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('y'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_yank_marked() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('Y'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_show_keybindings() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::F(1), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_clear_all() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('C'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_edit_comment() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('r'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_delete_comment() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('d'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_comment_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('c'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_next_error() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('e'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_prev_error() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('E'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_next_warning() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('w'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_prev_warning() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('W'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_normal_filter_include() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('i'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_normal_filter_exclude() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('o'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_enter_ui_mode() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('u'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_ui_exit() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_clear_search() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct NavigationKeybindings {
#[serde(default = "default_scroll_down")]
pub scroll_down: KeyBindings,
#[serde(default = "default_scroll_up")]
pub scroll_up: KeyBindings,
#[serde(default = "default_half_page_down")]
pub half_page_down: KeyBindings,
#[serde(default = "default_half_page_up")]
pub half_page_up: KeyBindings,
#[serde(default = "default_page_down")]
pub page_down: KeyBindings,
#[serde(default = "default_page_up")]
pub page_up: KeyBindings,
}
impl Default for NavigationKeybindings {
fn default() -> Self {
Self {
scroll_down: default_scroll_down(),
scroll_up: default_scroll_up(),
half_page_down: default_half_page_down(),
half_page_up: default_half_page_up(),
page_down: default_page_down(),
page_up: default_page_up(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct NormalKeybindings {
#[serde(default = "default_scroll_left")]
pub scroll_left: KeyBindings,
#[serde(default = "default_scroll_right")]
pub scroll_right: KeyBindings,
#[serde(default = "default_start_of_line")]
pub start_of_line: KeyBindings,
#[serde(default = "default_end_of_line")]
pub end_of_line: KeyBindings,
#[serde(default = "default_command_mode")]
pub command_mode: KeyBindings,
#[serde(default = "default_filter_mode_key")]
pub filter_mode: KeyBindings,
#[serde(default = "default_toggle_filtering")]
pub toggle_filtering: KeyBindings,
#[serde(default = "default_go_to_top_chord")]
pub go_to_top_chord: KeyBindings,
#[serde(default = "default_go_to_bottom")]
pub go_to_bottom: KeyBindings,
#[serde(default = "default_mark_line")]
pub mark_line: KeyBindings,
#[serde(default = "default_search_forward")]
pub search_forward: KeyBindings,
#[serde(default = "default_search_backward")]
pub search_backward: KeyBindings,
#[serde(default = "default_next_match")]
pub next_match: KeyBindings,
#[serde(default = "default_prev_match")]
pub prev_match: KeyBindings,
#[serde(default = "default_visual_mode")]
pub visual_mode: KeyBindings,
#[serde(default = "default_visual_char")]
pub visual_char: KeyBindings,
#[serde(default = "default_toggle_marks_only")]
pub toggle_marks_only: KeyBindings,
#[serde(default = "default_yank_line")]
pub yank_line: KeyBindings,
#[serde(default = "default_yank_marked")]
pub yank_marked: KeyBindings,
#[serde(default = "default_show_keybindings")]
pub show_keybindings: KeyBindings,
#[serde(default = "default_clear_all")]
pub clear_all: KeyBindings,
#[serde(default = "default_edit_comment")]
pub edit_comment: KeyBindings,
#[serde(default = "default_delete_comment")]
pub delete_comment: KeyBindings,
#[serde(default = "default_comment_line")]
pub comment_line: KeyBindings,
#[serde(default = "default_next_error")]
pub next_error: KeyBindings,
#[serde(default = "default_prev_error")]
pub prev_error: KeyBindings,
#[serde(default = "default_next_warning")]
pub next_warning: KeyBindings,
#[serde(default = "default_prev_warning")]
pub prev_warning: KeyBindings,
#[serde(default = "default_normal_filter_include")]
pub filter_include: KeyBindings,
#[serde(default = "default_normal_filter_exclude")]
pub filter_exclude: KeyBindings,
#[serde(default = "default_enter_ui_mode")]
pub enter_ui_mode: KeyBindings,
#[serde(default = "default_clear_search")]
pub clear_search: KeyBindings,
}
impl Default for NormalKeybindings {
fn default() -> Self {
Self {
scroll_left: default_scroll_left(),
scroll_right: default_scroll_right(),
start_of_line: default_start_of_line(),
end_of_line: default_end_of_line(),
command_mode: default_command_mode(),
filter_mode: default_filter_mode_key(),
toggle_filtering: default_toggle_filtering(),
go_to_top_chord: default_go_to_top_chord(),
go_to_bottom: default_go_to_bottom(),
mark_line: default_mark_line(),
search_forward: default_search_forward(),
search_backward: default_search_backward(),
next_match: default_next_match(),
prev_match: default_prev_match(),
visual_mode: default_visual_mode(),
visual_char: default_visual_char(),
toggle_marks_only: default_toggle_marks_only(),
yank_line: default_yank_line(),
yank_marked: default_yank_marked(),
show_keybindings: default_show_keybindings(),
clear_all: default_clear_all(),
edit_comment: default_edit_comment(),
delete_comment: default_delete_comment(),
comment_line: default_comment_line(),
next_error: default_next_error(),
prev_error: default_prev_error(),
next_warning: default_next_warning(),
prev_warning: default_prev_warning(),
filter_include: default_normal_filter_include(),
filter_exclude: default_normal_filter_exclude(),
enter_ui_mode: default_enter_ui_mode(),
clear_search: default_clear_search(),
}
}
}
#[inline(always)]
fn default_filter_toggle() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char(' '), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_delete() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('d'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_move_up() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('K'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_move_down() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('J'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_edit() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('e'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_set_color() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('c'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_toggle_all() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('A'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_clear_all() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('C'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_add_include() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('i'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_add_exclude() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('o'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_add_date() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('t'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_exit() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_sidebar_grow() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('>'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_sidebar_shrink() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('<'), KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct FilterKeybindings {
#[serde(default = "default_filter_toggle")]
pub toggle_filter: KeyBindings,
#[serde(default = "default_filter_delete")]
pub delete_filter: KeyBindings,
#[serde(default = "default_filter_move_up")]
pub move_filter_up: KeyBindings,
#[serde(default = "default_filter_move_down")]
pub move_filter_down: KeyBindings,
#[serde(default = "default_filter_edit")]
pub edit_filter: KeyBindings,
#[serde(default = "default_filter_set_color")]
pub set_color: KeyBindings,
#[serde(default = "default_filter_toggle_all")]
pub toggle_all_filters: KeyBindings,
#[serde(default = "default_filter_clear_all")]
pub clear_all_filters: KeyBindings,
#[serde(default = "default_filter_add_include")]
pub add_include_filter: KeyBindings,
#[serde(default = "default_filter_add_exclude")]
pub add_exclude_filter: KeyBindings,
#[serde(default = "default_filter_add_date")]
pub add_date_filter: KeyBindings,
#[serde(default = "default_filter_exit")]
pub exit_mode: KeyBindings,
#[serde(default = "default_filter_sidebar_grow")]
pub sidebar_grow: KeyBindings,
#[serde(default = "default_filter_sidebar_shrink")]
pub sidebar_shrink: KeyBindings,
}
impl Default for FilterKeybindings {
fn default() -> Self {
Self {
toggle_filter: default_filter_toggle(),
delete_filter: default_filter_delete(),
move_filter_up: default_filter_move_up(),
move_filter_down: default_filter_move_down(),
edit_filter: default_filter_edit(),
set_color: default_filter_set_color(),
toggle_all_filters: default_filter_toggle_all(),
clear_all_filters: default_filter_clear_all(),
add_include_filter: default_filter_add_include(),
add_exclude_filter: default_filter_add_exclude(),
add_date_filter: default_filter_add_date(),
exit_mode: default_filter_exit(),
sidebar_grow: default_filter_sidebar_grow(),
sidebar_shrink: default_filter_sidebar_shrink(),
}
}
}
#[inline(always)]
fn default_quit() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('q'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_next_tab() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Tab, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_prev_tab() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::BackTab, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_close_tab() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('w'), KeyModifiers::CONTROL)])
}
#[inline(always)]
fn default_new_tab() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('t'), KeyModifiers::CONTROL)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct GlobalKeybindings {
#[serde(default = "default_quit")]
pub quit: KeyBindings,
#[serde(default = "default_next_tab")]
pub next_tab: KeyBindings,
#[serde(default = "default_prev_tab")]
pub prev_tab: KeyBindings,
#[serde(default = "default_close_tab")]
pub close_tab: KeyBindings,
#[serde(default = "default_new_tab")]
pub new_tab: KeyBindings,
}
impl Default for GlobalKeybindings {
fn default() -> Self {
Self {
quit: default_quit(),
next_tab: default_next_tab(),
prev_tab: default_prev_tab(),
close_tab: default_close_tab(),
new_tab: default_new_tab(),
}
}
}
#[inline(always)]
fn default_comment_save() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('s'), KeyModifiers::CONTROL)])
}
#[inline(always)]
fn default_comment_newline() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_comment_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_comment_delete() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('d'), KeyModifiers::CONTROL)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct CommentKeybindings {
#[serde(default = "default_comment_save")]
pub save: KeyBindings,
#[serde(default = "default_comment_newline")]
pub newline: KeyBindings,
#[serde(default = "default_comment_cancel")]
pub cancel: KeyBindings,
#[serde(default = "default_comment_delete")]
pub delete: KeyBindings,
}
impl Default for CommentKeybindings {
fn default() -> Self {
Self {
save: default_comment_save(),
newline: default_comment_newline(),
cancel: default_comment_cancel(),
delete: default_comment_delete(),
}
}
}
#[inline(always)]
fn default_visual_comment() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('c'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_visual_yank() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('y'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_visual_mark() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('m'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_visual_exit() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_visual_line_search() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('/'), KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct VisualLineKeybindings {
#[serde(default = "default_visual_comment")]
pub comment: KeyBindings,
#[serde(default = "default_visual_yank")]
pub yank: KeyBindings,
#[serde(default = "default_visual_mark")]
pub mark: KeyBindings,
#[serde(default = "default_visual_line_search")]
pub search: KeyBindings,
#[serde(default = "default_visual_exit")]
pub exit: KeyBindings,
}
impl Default for VisualLineKeybindings {
fn default() -> Self {
Self {
comment: default_visual_comment(),
yank: default_visual_yank(),
mark: default_visual_mark(),
search: default_visual_line_search(),
exit: default_visual_exit(),
}
}
}
#[inline(always)]
fn default_vc_move_left() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('h'), KeyModifiers::NONE),
KeyBinding(KeyCode::Left, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_vc_move_right() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('l'), KeyModifiers::NONE),
KeyBinding(KeyCode::Right, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_vc_word_forward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('w'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_word_backward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('b'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_word_end() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('e'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_word_forward_big() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('W'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_word_backward_big() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('B'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_word_end_big() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('E'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_start_of_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('0'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_first_nonblank() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('^'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_end_of_line() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('$'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_find_forward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('f'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_find_backward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('F'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_till_forward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('t'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_till_backward() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('T'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_repeat_motion() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char(';'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_repeat_motion_rev() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char(','), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_filter_include() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('i'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_filter_exclude() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('o'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_search() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('/'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_yank() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('y'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_start_selection() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('v'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_exit() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct VisualKeybindings {
#[serde(default = "default_vc_move_left")]
pub move_left: KeyBindings,
#[serde(default = "default_vc_move_right")]
pub move_right: KeyBindings,
#[serde(default = "default_vc_word_forward")]
pub word_forward: KeyBindings,
#[serde(default = "default_vc_word_backward")]
pub word_backward: KeyBindings,
#[serde(default = "default_vc_word_end")]
pub word_end: KeyBindings,
#[serde(default = "default_vc_word_forward_big")]
pub word_forward_big: KeyBindings,
#[serde(default = "default_vc_word_backward_big")]
pub word_backward_big: KeyBindings,
#[serde(default = "default_vc_word_end_big")]
pub word_end_big: KeyBindings,
#[serde(default = "default_vc_start_of_line")]
pub start_of_line: KeyBindings,
#[serde(default = "default_vc_first_nonblank")]
pub first_nonblank: KeyBindings,
#[serde(default = "default_vc_end_of_line")]
pub end_of_line: KeyBindings,
#[serde(default = "default_vc_find_forward")]
pub find_forward: KeyBindings,
#[serde(default = "default_vc_find_backward")]
pub find_backward: KeyBindings,
#[serde(default = "default_vc_till_forward")]
pub till_forward: KeyBindings,
#[serde(default = "default_vc_till_backward")]
pub till_backward: KeyBindings,
#[serde(default = "default_vc_repeat_motion")]
pub repeat_motion: KeyBindings,
#[serde(default = "default_vc_repeat_motion_rev")]
pub repeat_motion_rev: KeyBindings,
#[serde(default = "default_vc_filter_include")]
pub filter_include: KeyBindings,
#[serde(default = "default_vc_filter_exclude")]
pub filter_exclude: KeyBindings,
#[serde(default = "default_vc_search")]
pub search: KeyBindings,
#[serde(default = "default_vc_start_selection")]
pub start_selection: KeyBindings,
#[serde(default = "default_vc_yank")]
pub yank: KeyBindings,
#[serde(default = "default_vc_exit")]
pub exit: KeyBindings,
}
impl Default for VisualKeybindings {
fn default() -> Self {
Self {
move_left: default_vc_move_left(),
move_right: default_vc_move_right(),
word_forward: default_vc_word_forward(),
word_backward: default_vc_word_backward(),
word_end: default_vc_word_end(),
word_forward_big: default_vc_word_forward_big(),
word_backward_big: default_vc_word_backward_big(),
word_end_big: default_vc_word_end_big(),
start_of_line: default_vc_start_of_line(),
first_nonblank: default_vc_first_nonblank(),
end_of_line: default_vc_end_of_line(),
find_forward: default_vc_find_forward(),
find_backward: default_vc_find_backward(),
till_forward: default_vc_till_forward(),
till_backward: default_vc_till_backward(),
repeat_motion: default_vc_repeat_motion(),
repeat_motion_rev: default_vc_repeat_motion_rev(),
filter_include: default_vc_filter_include(),
filter_exclude: default_vc_filter_exclude(),
search: default_vc_search(),
start_selection: default_vc_start_selection(),
yank: default_vc_yank(),
exit: default_vc_exit(),
}
}
}
#[inline(always)]
fn default_search_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_search_confirm() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SearchKeybindings {
#[serde(default = "default_search_cancel")]
pub cancel: KeyBindings,
#[serde(default = "default_search_confirm")]
pub confirm: KeyBindings,
}
impl Default for SearchKeybindings {
fn default() -> Self {
Self {
cancel: default_search_cancel(),
confirm: default_search_confirm(),
}
}
}
#[inline(always)]
fn default_filter_edit_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_filter_edit_confirm() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct FilterEditKeybindings {
#[serde(default = "default_filter_edit_cancel")]
pub cancel: KeyBindings,
#[serde(default = "default_filter_edit_confirm")]
pub confirm: KeyBindings,
}
impl Default for FilterEditKeybindings {
fn default() -> Self {
Self {
cancel: default_filter_edit_cancel(),
confirm: default_filter_edit_confirm(),
}
}
}
#[inline(always)]
fn default_command_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_command_confirm() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct CommandModeKeybindings {
#[serde(default = "default_command_cancel")]
pub cancel: KeyBindings,
#[serde(default = "default_command_confirm")]
pub confirm: KeyBindings,
}
impl Default for CommandModeKeybindings {
fn default() -> Self {
Self {
cancel: default_command_cancel(),
confirm: default_command_confirm(),
}
}
}
#[inline(always)]
fn default_docker_confirm() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_docker_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DockerSelectKeybindings {
#[serde(default = "default_docker_confirm")]
pub confirm: KeyBindings,
#[serde(default = "default_docker_cancel")]
pub cancel: KeyBindings,
}
impl Default for DockerSelectKeybindings {
fn default() -> Self {
Self {
confirm: default_docker_confirm(),
cancel: default_docker_cancel(),
}
}
}
#[inline(always)]
fn default_dlt_confirm() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_dlt_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_dlt_delete() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('d'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_dlt_tab() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Tab, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_dlt_backtab() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::BackTab, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DltSelectKeybindings {
#[serde(default = "default_dlt_confirm")]
pub confirm: KeyBindings,
#[serde(default = "default_dlt_cancel")]
pub cancel: KeyBindings,
#[serde(default = "default_dlt_delete")]
pub delete: KeyBindings,
#[serde(default = "default_dlt_tab")]
pub next_field: KeyBindings,
#[serde(default = "default_dlt_backtab")]
pub prev_field: KeyBindings,
}
impl Default for DltSelectKeybindings {
fn default() -> Self {
Self {
confirm: default_dlt_confirm(),
cancel: default_dlt_cancel(),
delete: default_dlt_delete(),
next_field: default_dlt_tab(),
prev_field: default_dlt_backtab(),
}
}
}
#[inline(always)]
fn default_vc_toggle() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char(' '), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_all() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('a'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_none() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('n'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_apply() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_vc_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct ValueColorsKeybindings {
#[serde(default = "default_vc_toggle")]
pub toggle: KeyBindings,
#[serde(default = "default_vc_all")]
pub all: KeyBindings,
#[serde(default = "default_vc_none")]
pub none: KeyBindings,
#[serde(default = "default_vc_apply")]
pub apply: KeyBindings,
#[serde(default = "default_vc_cancel")]
pub cancel: KeyBindings,
}
impl Default for ValueColorsKeybindings {
fn default() -> Self {
Self {
toggle: default_vc_toggle(),
all: default_vc_all(),
none: default_vc_none(),
apply: default_vc_apply(),
cancel: default_vc_cancel(),
}
}
}
#[inline(always)]
fn default_sf_toggle() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char(' '), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_sf_move_up() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('K'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_sf_move_down() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('J'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_sf_all() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('a'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_sf_none() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('n'), KeyModifiers::NONE)])
}
#[inline(always)]
fn default_sf_apply() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Enter, KeyModifiers::NONE)])
}
#[inline(always)]
fn default_sf_cancel() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Esc, KeyModifiers::NONE)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SelectFieldsKeybindings {
#[serde(default = "default_sf_toggle")]
pub toggle: KeyBindings,
#[serde(default = "default_sf_move_up")]
pub move_up: KeyBindings,
#[serde(default = "default_sf_move_down")]
pub move_down: KeyBindings,
#[serde(default = "default_sf_all")]
pub all: KeyBindings,
#[serde(default = "default_sf_none")]
pub none: KeyBindings,
#[serde(default = "default_sf_apply")]
pub apply: KeyBindings,
#[serde(default = "default_sf_cancel")]
pub cancel: KeyBindings,
}
impl Default for SelectFieldsKeybindings {
fn default() -> Self {
Self {
toggle: default_sf_toggle(),
move_up: default_sf_move_up(),
move_down: default_sf_move_down(),
all: default_sf_all(),
none: default_sf_none(),
apply: default_sf_apply(),
cancel: default_sf_cancel(),
}
}
}
#[inline(always)]
fn default_help_close() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('q'), KeyModifiers::NONE),
KeyBinding(KeyCode::Esc, KeyModifiers::NONE),
])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct HelpKeybindings {
#[serde(default = "default_help_close")]
pub close: KeyBindings,
}
impl Default for HelpKeybindings {
fn default() -> Self {
Self {
close: default_help_close(),
}
}
}
#[inline(always)]
fn default_confirm_yes() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('y'), KeyModifiers::NONE),
KeyBinding(KeyCode::Enter, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_confirm_no() -> KeyBindings {
KeyBindings(vec![
KeyBinding(KeyCode::Char('n'), KeyModifiers::NONE),
KeyBinding(KeyCode::Esc, KeyModifiers::NONE),
])
}
#[inline(always)]
fn default_confirm_always() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('Y'), KeyModifiers::SHIFT)])
}
#[inline(always)]
fn default_confirm_never() -> KeyBindings {
KeyBindings(vec![KeyBinding(KeyCode::Char('N'), KeyModifiers::SHIFT)])
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct ConfirmKeybindings {
#[serde(default = "default_confirm_yes")]
pub yes: KeyBindings,
#[serde(default = "default_confirm_no")]
pub no: KeyBindings,
#[serde(default = "default_confirm_always")]
pub always: KeyBindings,
#[serde(default = "default_confirm_never")]
pub never: KeyBindings,
}
impl Default for ConfirmKeybindings {
fn default() -> Self {
Self {
yes: default_confirm_yes(),
no: default_confirm_no(),
always: default_confirm_always(),
never: default_confirm_never(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct UiKeybindings {
#[serde(default = "default_toggle_sidebar")]
pub toggle_sidebar: KeyBindings,
#[serde(default = "default_toggle_mode_bar")]
pub toggle_mode_bar: KeyBindings,
#[serde(default = "default_toggle_borders")]
pub toggle_borders: KeyBindings,
#[serde(default = "default_toggle_wrap")]
pub toggle_wrap: KeyBindings,
#[serde(default = "default_ui_exit")]
pub exit: KeyBindings,
}
impl Default for UiKeybindings {
fn default() -> Self {
Self {
toggle_sidebar: default_toggle_sidebar(),
toggle_mode_bar: default_toggle_mode_bar(),
toggle_borders: default_toggle_borders(),
toggle_wrap: default_toggle_wrap(),
exit: default_ui_exit(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Keybindings {
#[serde(default)]
pub navigation: NavigationKeybindings,
#[serde(default)]
pub normal: NormalKeybindings,
#[serde(default)]
pub filter: FilterKeybindings,
#[serde(default)]
pub global: GlobalKeybindings,
#[serde(default)]
pub comment: CommentKeybindings,
#[serde(default)]
pub visual_line: VisualLineKeybindings,
#[serde(default)]
pub visual: VisualKeybindings,
#[serde(default)]
pub search: SearchKeybindings,
#[serde(default)]
pub filter_edit: FilterEditKeybindings,
#[serde(default)]
pub command: CommandModeKeybindings,
#[serde(default)]
pub docker_select: DockerSelectKeybindings,
#[serde(default)]
pub dlt_select: DltSelectKeybindings,
#[serde(default)]
pub value_colors: ValueColorsKeybindings,
#[serde(default)]
pub select_fields: SelectFieldsKeybindings,
#[serde(default)]
pub help: HelpKeybindings,
#[serde(default)]
pub confirm: ConfirmKeybindings,
#[serde(default)]
pub ui: UiKeybindings,
}
impl Keybindings {
pub fn validate(&self) -> Vec<String> {
let mut conflicts = Vec::new();
let nav = &self.navigation;
let normal_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("navigation.half_page_down", &nav.half_page_down),
("navigation.half_page_up", &nav.half_page_up),
("navigation.page_down", &nav.page_down),
("navigation.page_up", &nav.page_up),
("normal.scroll_left", &self.normal.scroll_left),
("normal.scroll_right", &self.normal.scroll_right),
("normal.start_of_line", &self.normal.start_of_line),
("normal.end_of_line", &self.normal.end_of_line),
("normal.command_mode", &self.normal.command_mode),
("normal.filter_mode", &self.normal.filter_mode),
("normal.toggle_filtering", &self.normal.toggle_filtering),
("normal.enter_ui_mode", &self.normal.enter_ui_mode),
("normal.filter_include", &self.normal.filter_include),
("normal.filter_exclude", &self.normal.filter_exclude),
("normal.go_to_top_chord", &self.normal.go_to_top_chord),
("normal.go_to_bottom", &self.normal.go_to_bottom),
("normal.mark_line", &self.normal.mark_line),
("normal.toggle_marks_only", &self.normal.toggle_marks_only),
("normal.yank_line", &self.normal.yank_line),
("normal.yank_marked", &self.normal.yank_marked),
("normal.visual_mode", &self.normal.visual_mode),
("normal.visual_char", &self.normal.visual_char),
("normal.search_forward", &self.normal.search_forward),
("normal.search_backward", &self.normal.search_backward),
("normal.next_match", &self.normal.next_match),
("normal.prev_match", &self.normal.prev_match),
("normal.show_keybindings", &self.normal.show_keybindings),
("normal.clear_all", &self.normal.clear_all),
("normal.edit_comment", &self.normal.edit_comment),
("normal.delete_comment", &self.normal.delete_comment),
("normal.comment_line", &self.normal.comment_line),
("normal.next_error", &self.normal.next_error),
("normal.prev_error", &self.normal.prev_error),
("normal.next_warning", &self.normal.next_warning),
("normal.prev_warning", &self.normal.prev_warning),
("global.quit", &self.global.quit),
("global.next_tab", &self.global.next_tab),
("global.prev_tab", &self.global.prev_tab),
("global.close_tab", &self.global.close_tab),
("global.new_tab", &self.global.new_tab),
];
let filter_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("filter.toggle_filter", &self.filter.toggle_filter),
("filter.delete_filter", &self.filter.delete_filter),
("filter.move_filter_up", &self.filter.move_filter_up),
("filter.move_filter_down", &self.filter.move_filter_down),
("filter.edit_filter", &self.filter.edit_filter),
("filter.set_color", &self.filter.set_color),
("filter.toggle_all_filters", &self.filter.toggle_all_filters),
("filter.clear_all_filters", &self.filter.clear_all_filters),
("filter.add_include_filter", &self.filter.add_include_filter),
("filter.add_exclude_filter", &self.filter.add_exclude_filter),
("filter.add_date_filter", &self.filter.add_date_filter),
("filter.exit_mode", &self.filter.exit_mode),
("global.quit", &self.global.quit),
("global.next_tab", &self.global.next_tab),
("global.prev_tab", &self.global.prev_tab),
];
let visual_line_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("visual_line.comment", &self.visual_line.comment),
("visual_line.yank", &self.visual_line.yank),
("visual_line.mark", &self.visual_line.mark),
("visual_line.search", &self.visual_line.search),
("visual_line.exit", &self.visual_line.exit),
];
let visual_char_actions: &[(&str, &KeyBindings)] = &[
("visual.move_left", &self.visual.move_left),
("visual.move_right", &self.visual.move_right),
("visual.word_forward", &self.visual.word_forward),
("visual.word_backward", &self.visual.word_backward),
("visual.word_end", &self.visual.word_end),
("visual.word_forward_big", &self.visual.word_forward_big),
("visual.word_backward_big", &self.visual.word_backward_big),
("visual.word_end_big", &self.visual.word_end_big),
("visual.start_of_line", &self.visual.start_of_line),
("visual.first_nonblank", &self.visual.first_nonblank),
("visual.end_of_line", &self.visual.end_of_line),
("visual.find_forward", &self.visual.find_forward),
("visual.find_backward", &self.visual.find_backward),
("visual.till_forward", &self.visual.till_forward),
("visual.till_backward", &self.visual.till_backward),
("visual.repeat_motion", &self.visual.repeat_motion),
("visual.repeat_motion_rev", &self.visual.repeat_motion_rev),
("visual.filter_include", &self.visual.filter_include),
("visual.filter_exclude", &self.visual.filter_exclude),
("visual.search", &self.visual.search),
("visual.start_selection", &self.visual.start_selection),
("visual.yank", &self.visual.yank),
("visual.exit", &self.visual.exit),
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("navigation.half_page_down", &nav.half_page_down),
("navigation.half_page_up", &nav.half_page_up),
("navigation.page_down", &nav.page_down),
("navigation.page_up", &nav.page_up),
("normal.go_to_bottom", &self.normal.go_to_bottom),
("normal.go_to_top_chord", &self.normal.go_to_top_chord),
];
let docker_select_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("docker_select.confirm", &self.docker_select.confirm),
("docker_select.cancel", &self.docker_select.cancel),
];
let dlt_select_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("dlt_select.confirm", &self.dlt_select.confirm),
("dlt_select.cancel", &self.dlt_select.cancel),
("dlt_select.delete", &self.dlt_select.delete),
];
let value_colors_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("value_colors.toggle", &self.value_colors.toggle),
("value_colors.all", &self.value_colors.all),
("value_colors.none", &self.value_colors.none),
("value_colors.apply", &self.value_colors.apply),
("value_colors.cancel", &self.value_colors.cancel),
];
let select_fields_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("select_fields.toggle", &self.select_fields.toggle),
("select_fields.move_up", &self.select_fields.move_up),
("select_fields.move_down", &self.select_fields.move_down),
("select_fields.all", &self.select_fields.all),
("select_fields.none", &self.select_fields.none),
("select_fields.apply", &self.select_fields.apply),
("select_fields.cancel", &self.select_fields.cancel),
];
let help_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("navigation.half_page_down", &nav.half_page_down),
("navigation.half_page_up", &nav.half_page_up),
("help.close", &self.help.close),
];
let ui_actions: &[(&str, &KeyBindings)] = &[
("navigation.scroll_down", &nav.scroll_down),
("navigation.scroll_up", &nav.scroll_up),
("ui.toggle_sidebar", &self.ui.toggle_sidebar),
("ui.toggle_mode_bar", &self.ui.toggle_mode_bar),
("ui.toggle_borders", &self.ui.toggle_borders),
("ui.toggle_wrap", &self.ui.toggle_wrap),
("ui.exit", &self.ui.exit),
("global.quit", &self.global.quit),
("global.next_tab", &self.global.next_tab),
("global.prev_tab", &self.global.prev_tab),
];
check_conflicts(normal_actions, &mut conflicts);
check_conflicts(filter_actions, &mut conflicts);
check_conflicts(visual_line_actions, &mut conflicts);
check_conflicts(visual_char_actions, &mut conflicts);
check_conflicts(docker_select_actions, &mut conflicts);
check_conflicts(dlt_select_actions, &mut conflicts);
check_conflicts(value_colors_actions, &mut conflicts);
check_conflicts(select_fields_actions, &mut conflicts);
check_conflicts(help_actions, &mut conflicts);
check_conflicts(ui_actions, &mut conflicts);
conflicts
}
}
pub(super) fn check_conflicts(actions: &[(&str, &KeyBindings)], out: &mut Vec<String>) {
for i in 0..actions.len() {
for j in (i + 1)..actions.len() {
let (name_a, kb_a) = actions[i];
let (name_b, kb_b) = actions[j];
if kb_a.has_overlap(kb_b) {
let key_str = kb_a
.0
.iter()
.find(|a| kb_b.0.iter().any(|b| a.0 == b.0 && a.1 == b.1))
.map(|b| b.display())
.unwrap_or_default();
out.push(format!(
"keybinding conflict: '{}' and '{}' both use '{}'",
name_a, name_b, key_str
));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crossterm::event::{KeyCode, KeyModifiers};
fn kb(key: KeyCode) -> KeyBindings {
KeyBindings(vec![KeyBinding(key, KeyModifiers::NONE)])
}
fn kb2(key1: KeyCode, key2: KeyCode) -> KeyBindings {
KeyBindings(vec![
KeyBinding(key1, KeyModifiers::NONE),
KeyBinding(key2, KeyModifiers::NONE),
])
}
#[test]
fn test_keybindings_matches_any() {
let bindings = kb2(KeyCode::Char('j'), KeyCode::Down);
assert!(bindings.matches(KeyCode::Char('j'), KeyModifiers::NONE));
assert!(bindings.matches(KeyCode::Down, KeyModifiers::NONE));
assert!(!bindings.matches(KeyCode::Char('k'), KeyModifiers::NONE));
}
#[test]
fn test_keybindings_empty_never_matches() {
assert!(!KeyBindings(vec![]).matches(KeyCode::Char('j'), KeyModifiers::NONE));
}
#[test]
fn test_keybindings_display_single() {
assert_eq!(kb(KeyCode::Char('j')).display(), "j");
}
#[test]
fn test_keybindings_display_multiple() {
assert_eq!(kb2(KeyCode::Char('j'), KeyCode::Down).display(), "j/Down");
}
#[test]
fn test_keybindings_display_empty() {
assert_eq!(KeyBindings(vec![]).display(), "");
}
#[test]
fn test_has_overlap_true() {
let a = kb2(KeyCode::Char('j'), KeyCode::Down);
let b = kb(KeyCode::Down);
assert!(a.has_overlap(&b));
}
#[test]
fn test_has_overlap_false() {
assert!(!kb(KeyCode::Char('j')).has_overlap(&kb(KeyCode::Char('k'))));
}
#[test]
fn test_has_overlap_empty() {
assert!(!KeyBindings(vec![]).has_overlap(&kb(KeyCode::Char('j'))));
}
#[test]
fn test_keybindings_deserialize_single_string() {
let bindings: KeyBindings = serde_json::from_str(r#""j""#).unwrap();
assert_eq!(
bindings.0,
vec![KeyBinding(KeyCode::Char('j'), KeyModifiers::NONE)]
);
}
#[test]
fn test_keybindings_deserialize_array() {
let bindings: KeyBindings = serde_json::from_str(r#"["j", "Down"]"#).unwrap();
assert_eq!(
bindings.0,
vec![
KeyBinding(KeyCode::Char('j'), KeyModifiers::NONE),
KeyBinding(KeyCode::Down, KeyModifiers::NONE),
]
);
}
#[test]
fn test_keybindings_serialize_single_as_string() {
let json = serde_json::to_string(&kb(KeyCode::Char('j'))).unwrap();
assert_eq!(json, r#""j""#);
}
#[test]
fn test_keybindings_serialize_multiple_as_array() {
let json = serde_json::to_string(&kb2(KeyCode::Char('j'), KeyCode::Down)).unwrap();
assert_eq!(json, r#"["j","Down"]"#);
}
#[test]
fn test_check_conflicts_detects_overlap() {
let a = kb(KeyCode::Char('j'));
let b = kb(KeyCode::Char('j'));
let actions: &[(&str, &KeyBindings)] = &[("action.a", &a), ("action.b", &b)];
let mut out = Vec::new();
check_conflicts(actions, &mut out);
assert_eq!(out.len(), 1);
assert!(out[0].contains("action.a"));
assert!(out[0].contains("action.b"));
assert!(out[0].contains("'j'"));
}
#[test]
fn test_check_conflicts_no_overlap() {
let a = kb(KeyCode::Char('j'));
let b = kb(KeyCode::Char('k'));
let actions: &[(&str, &KeyBindings)] = &[("action.a", &a), ("action.b", &b)];
let mut out = Vec::new();
check_conflicts(actions, &mut out);
assert!(out.is_empty());
}
#[test]
fn test_default_keybindings_no_conflicts() {
let conflicts = Keybindings::default().validate();
assert!(
conflicts.is_empty(),
"Default keybindings have conflicts: {:?}",
conflicts
);
}
#[test]
fn test_default_scroll_bindings() {
let nav = NavigationKeybindings::default();
assert!(
nav.scroll_down
.matches(KeyCode::Char('j'), KeyModifiers::NONE)
);
assert!(nav.scroll_down.matches(KeyCode::Down, KeyModifiers::NONE));
assert!(
nav.scroll_up
.matches(KeyCode::Char('k'), KeyModifiers::NONE)
);
assert!(nav.scroll_up.matches(KeyCode::Up, KeyModifiers::NONE));
}
}