use super::{Action, KeybindingMode, Keybindings};
pub fn default_keybindings() -> Keybindings {
let mut kb = Keybindings::new();
add_normal_mode(&mut kb);
add_help_mode(&mut kb);
add_theme_picker_mode(&mut kb);
add_interactive_mode(&mut kb);
add_interactive_table_mode(&mut kb);
add_link_follow_mode(&mut kb);
add_link_search_mode(&mut kb);
add_file_picker_mode(&mut kb);
add_file_search_mode(&mut kb);
add_search_mode(&mut kb);
add_doc_search_mode(&mut kb);
add_command_palette_mode(&mut kb);
add_confirm_dialog_mode(&mut kb);
add_cell_edit_mode(&mut kb);
kb
}
fn bind(kb: &mut Keybindings, mode: KeybindingMode, key: &str, action: Action) {
kb.bind(mode, key, action)
.unwrap_or_else(|e| panic!("Invalid default keybinding '{}': {}", key, e));
}
fn add_normal_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::Normal;
bind(kb, Normal, "j", Next);
bind(kb, Normal, "Down", Next);
bind(kb, Normal, "k", Previous);
bind(kb, Normal, "Up", Previous);
bind(kb, Normal, "g", First);
bind(kb, Normal, "G", Last);
bind(kb, Normal, "d", PageDown);
bind(kb, Normal, "PageDown", PageDown);
bind(kb, Normal, "u", PageUp);
bind(kb, Normal, "PageUp", PageUp);
bind(kb, Normal, "Home", First);
bind(kb, Normal, "End", Last);
bind(kb, Normal, "p", JumpToParent);
bind(kb, Normal, "Enter", ToggleExpand);
bind(kb, Normal, "Space", ToggleExpand);
bind(kb, Normal, "Tab", ToggleFocus);
bind(kb, Normal, "Shift+Tab", ToggleFocusBack);
bind(kb, Normal, "h", Collapse);
bind(kb, Normal, "Left", Collapse);
bind(kb, Normal, "l", Expand);
bind(kb, Normal, "Right", Expand);
bind(kb, Normal, "w", ToggleOutline);
bind(kb, Normal, "[", OutlineWidthDecrease);
bind(kb, Normal, "]", OutlineWidthIncrease);
bind(kb, Normal, "T", ToggleTodoFilter);
bind(kb, Normal, "#", ToggleHeadingMarkers);
bind(kb, Normal, "m", SetBookmark);
bind(kb, Normal, "'", JumpToBookmark);
bind(kb, Normal, "i", EnterInteractiveMode);
bind(kb, Normal, "f", EnterLinkFollowMode);
bind(kb, Normal, "s", EnterSearchMode);
bind(kb, Normal, "/", EnterDocSearch);
bind(kb, Normal, ":", OpenCommandPalette);
bind(kb, Normal, "r", ToggleRawSource);
bind(kb, Normal, "M", ToggleMouseCapture);
bind(kb, Normal, "t", ToggleThemePicker);
bind(kb, Normal, "?", ToggleHelp);
bind(kb, Normal, "y", CopyContent);
bind(kb, Normal, "Y", CopyAnchor);
bind(kb, Normal, "b", GoBack);
bind(kb, Normal, "Backspace", GoBack);
bind(kb, Normal, "F", GoForward);
bind(kb, Normal, "e", OpenInEditor);
bind(kb, Normal, "Ctrl+o", OpenFilePicker);
bind(kb, Normal, "o", OpenFilePicker);
bind(kb, Normal, "q", Quit);
bind(kb, Normal, "Escape", Quit);
bind(kb, Normal, "Ctrl+l", Redraw);
bind(kb, Normal, "1", JumpToHeading1);
bind(kb, Normal, "2", JumpToHeading2);
bind(kb, Normal, "3", JumpToHeading3);
bind(kb, Normal, "4", JumpToHeading4);
bind(kb, Normal, "5", JumpToHeading5);
bind(kb, Normal, "6", JumpToHeading6);
bind(kb, Normal, "7", JumpToHeading7);
bind(kb, Normal, "8", JumpToHeading8);
bind(kb, Normal, "9", JumpToHeading9);
bind(kb, Normal, "n", NextMatch);
bind(kb, Normal, "N", PrevMatch);
}
fn add_help_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::Help;
bind(kb, Help, "j", HelpScrollDown);
bind(kb, Help, "Down", HelpScrollDown);
bind(kb, Help, "k", HelpScrollUp);
bind(kb, Help, "Up", HelpScrollUp);
bind(kb, Help, "d", PageDown);
bind(kb, Help, "PageDown", PageDown);
bind(kb, Help, "u", PageUp);
bind(kb, Help, "PageUp", PageUp);
bind(kb, Help, "g", First);
bind(kb, Help, "G", Last);
bind(kb, Help, "Home", First);
bind(kb, Help, "End", Last);
bind(kb, Help, "?", ToggleHelp);
bind(kb, Help, "Escape", ToggleHelp);
bind(kb, Help, "y", CopyContent);
bind(kb, Help, "Y", CopyAnchor);
bind(kb, Help, "q", Quit);
}
fn add_theme_picker_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::ThemePicker;
bind(kb, ThemePicker, "j", ThemePickerNext);
bind(kb, ThemePicker, "Down", ThemePickerNext);
bind(kb, ThemePicker, "k", ThemePickerPrevious);
bind(kb, ThemePicker, "Up", ThemePickerPrevious);
bind(kb, ThemePicker, "Enter", ApplyTheme);
bind(kb, ThemePicker, "Escape", ToggleThemePicker);
bind(kb, ThemePicker, "y", CopyContent);
bind(kb, ThemePicker, "Y", CopyAnchor);
bind(kb, ThemePicker, "q", Quit);
}
fn add_interactive_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::Interactive;
bind(kb, Interactive, "Escape", ExitInteractiveMode);
bind(kb, Interactive, "i", ExitInteractiveMode);
bind(kb, Interactive, "j", InteractiveNext);
bind(kb, Interactive, "Down", InteractiveNext);
bind(kb, Interactive, "k", InteractivePrevious);
bind(kb, Interactive, "Up", InteractivePrevious);
bind(kb, Interactive, "Tab", InteractiveNextLink);
bind(kb, Interactive, "Shift+Tab", InteractivePreviousLink);
bind(kb, Interactive, "Enter", InteractiveActivate);
bind(kb, Interactive, "Space", InteractiveActivate);
bind(kb, Interactive, "d", PageDown);
bind(kb, Interactive, "PageDown", PageDown);
bind(kb, Interactive, "u", PageUp);
bind(kb, Interactive, "PageUp", PageUp);
bind(kb, Interactive, "g", First);
bind(kb, Interactive, "G", Last);
bind(kb, Interactive, "Home", First);
bind(kb, Interactive, "End", Last);
bind(kb, Interactive, "/", EnterDocSearch);
bind(kb, Interactive, "n", NextMatch);
bind(kb, Interactive, "N", PrevMatch);
bind(kb, Interactive, "y", CopyContent);
bind(kb, Interactive, "Ctrl+z", UndoEdit);
bind(kb, Interactive, "e", OpenInEditor);
bind(kb, Interactive, "q", Quit);
}
fn add_interactive_table_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::InteractiveTable;
bind(kb, InteractiveTable, "Escape", ExitMode);
bind(kb, InteractiveTable, "h", InteractiveLeft);
bind(kb, InteractiveTable, "Left", InteractiveLeft);
bind(kb, InteractiveTable, "l", InteractiveRight);
bind(kb, InteractiveTable, "Right", InteractiveRight);
bind(kb, InteractiveTable, "j", InteractiveNext);
bind(kb, InteractiveTable, "Down", InteractiveNext);
bind(kb, InteractiveTable, "k", InteractivePrevious);
bind(kb, InteractiveTable, "Up", InteractivePrevious);
bind(kb, InteractiveTable, "y", CopyTableCell);
bind(kb, InteractiveTable, "Y", CopyTableRow);
bind(kb, InteractiveTable, "r", CopyTableMarkdown);
bind(kb, InteractiveTable, "Enter", InteractiveActivate);
bind(kb, InteractiveTable, "Ctrl+z", UndoEdit);
bind(kb, InteractiveTable, "q", Quit);
}
fn add_link_follow_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::LinkFollow;
bind(kb, LinkFollow, "Escape", ExitMode);
bind(kb, LinkFollow, "j", NextLink);
bind(kb, LinkFollow, "Down", NextLink);
bind(kb, LinkFollow, "Tab", NextLink);
bind(kb, LinkFollow, "k", PreviousLink);
bind(kb, LinkFollow, "Up", PreviousLink);
bind(kb, LinkFollow, "Shift+Tab", PreviousLink);
bind(kb, LinkFollow, "Enter", FollowLink);
bind(kb, LinkFollow, "/", LinkSearch);
bind(kb, LinkFollow, "p", JumpToParent);
bind(kb, LinkFollow, "1", JumpToLink1);
bind(kb, LinkFollow, "2", JumpToLink2);
bind(kb, LinkFollow, "3", JumpToLink3);
bind(kb, LinkFollow, "4", JumpToLink4);
bind(kb, LinkFollow, "5", JumpToLink5);
bind(kb, LinkFollow, "6", JumpToLink6);
bind(kb, LinkFollow, "7", JumpToLink7);
bind(kb, LinkFollow, "8", JumpToLink8);
bind(kb, LinkFollow, "9", JumpToLink9);
bind(kb, LinkFollow, "y", CopyContent);
bind(kb, LinkFollow, "Y", CopyAnchor);
bind(kb, LinkFollow, "q", Quit);
}
fn add_link_search_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::LinkSearch;
bind(kb, LinkSearch, "Escape", ExitMode);
bind(kb, LinkSearch, "Enter", FollowLink);
bind(kb, LinkSearch, "Down", NextLink);
bind(kb, LinkSearch, "Up", PreviousLink);
bind(kb, LinkSearch, "Backspace", SearchBackspace);
}
fn add_search_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::Search;
bind(kb, Search, "Escape", ExitMode);
bind(kb, Search, "Enter", ConfirmAction);
bind(kb, Search, "Backspace", SearchBackspace);
bind(kb, Search, "Tab", ToggleSearchMode);
}
fn add_doc_search_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::DocSearch;
bind(kb, DocSearch, "Escape", ExitMode);
bind(kb, DocSearch, "Enter", ConfirmAction);
bind(kb, DocSearch, "Backspace", SearchBackspace);
bind(kb, DocSearch, "n", NextMatch);
bind(kb, DocSearch, "N", PrevMatch);
bind(kb, DocSearch, "Down", NextMatch);
bind(kb, DocSearch, "Up", PrevMatch);
bind(kb, DocSearch, "Tab", ToggleSearchMode);
bind(kb, DocSearch, "Shift+Tab", PrevMatch);
bind(kb, DocSearch, "/", EnterDocSearch);
}
fn add_command_palette_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::CommandPalette;
bind(kb, CommandPalette, "Escape", ExitMode);
bind(kb, CommandPalette, "Enter", ConfirmAction);
bind(kb, CommandPalette, "Down", CommandPaletteNext);
bind(kb, CommandPalette, "j", CommandPaletteNext);
bind(kb, CommandPalette, "Up", CommandPalettePrev);
bind(kb, CommandPalette, "k", CommandPalettePrev);
bind(kb, CommandPalette, "Tab", CommandPaletteAutocomplete);
bind(kb, CommandPalette, "Backspace", SearchBackspace);
}
fn add_confirm_dialog_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::ConfirmDialog;
bind(kb, ConfirmDialog, "y", ConfirmAction);
bind(kb, ConfirmDialog, "Y", ConfirmAction);
bind(kb, ConfirmDialog, "Enter", ConfirmAction);
bind(kb, ConfirmDialog, "n", CancelAction);
bind(kb, ConfirmDialog, "N", CancelAction);
bind(kb, ConfirmDialog, "Escape", CancelAction);
bind(kb, ConfirmDialog, "q", DiscardAndQuit);
bind(kb, ConfirmDialog, "d", DiscardAndContinue);
}
fn add_cell_edit_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::CellEdit;
bind(kb, CellEdit, "Escape", CancelAction);
bind(kb, CellEdit, "Enter", ConfirmAction);
bind(kb, CellEdit, "Backspace", SearchBackspace);
}
fn add_file_picker_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::FilePicker;
bind(kb, FilePicker, "j", Next);
bind(kb, FilePicker, "Down", Next);
bind(kb, FilePicker, "k", Previous);
bind(kb, FilePicker, "Up", Previous);
bind(kb, FilePicker, "Tab", Next);
bind(kb, FilePicker, "Shift+Tab", Previous);
bind(kb, FilePicker, "g", First);
bind(kb, FilePicker, "G", Last);
bind(kb, FilePicker, "Home", First);
bind(kb, FilePicker, "End", Last);
bind(kb, FilePicker, "Enter", FollowLink);
bind(kb, FilePicker, "Backspace", ParentDirectory);
bind(kb, FilePicker, "/", LinkSearch);
bind(kb, FilePicker, "h", ToggleHidden);
bind(kb, FilePicker, "Escape", ExitMode);
bind(kb, FilePicker, "q", Quit);
}
fn add_file_search_mode(kb: &mut Keybindings) {
use Action::*;
use KeybindingMode::FileSearch;
bind(kb, FileSearch, "Tab", Next);
bind(kb, FileSearch, "Down", Next);
bind(kb, FileSearch, "Up", Previous);
bind(kb, FileSearch, "Escape", ExitMode);
bind(kb, FileSearch, "Enter", FollowLink);
bind(kb, FileSearch, "Backspace", SearchBackspace);
}
#[cfg(test)]
mod tests {
use super::*;
use crossterm::event::{KeyCode, KeyEventKind, KeyEventState, KeyModifiers};
fn make_key_event(code: KeyCode, modifiers: KeyModifiers) -> crossterm::event::KeyEvent {
crossterm::event::KeyEvent {
code,
modifiers,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}
}
#[test]
fn test_default_normal_mode() {
let mut kb = default_keybindings();
assert_eq!(
kb.dispatch(
KeybindingMode::Normal,
make_key_event(KeyCode::Char('j'), KeyModifiers::NONE)
),
Some(Action::Next)
);
assert_eq!(
kb.dispatch(
KeybindingMode::Normal,
make_key_event(KeyCode::Char('k'), KeyModifiers::NONE)
),
Some(Action::Previous)
);
assert_eq!(
kb.dispatch(
KeybindingMode::Normal,
make_key_event(KeyCode::Char('q'), KeyModifiers::NONE)
),
Some(Action::Quit)
);
assert_eq!(
kb.dispatch(
KeybindingMode::Normal,
make_key_event(KeyCode::Char('?'), KeyModifiers::NONE)
),
Some(Action::ToggleHelp)
);
}
#[test]
fn test_default_table_mode_copy_bindings() {
let mut kb = default_keybindings();
assert_eq!(
kb.dispatch(
KeybindingMode::InteractiveTable,
make_key_event(KeyCode::Char('y'), KeyModifiers::NONE)
),
Some(Action::CopyTableCell)
);
assert_eq!(
kb.dispatch(
KeybindingMode::InteractiveTable,
make_key_event(KeyCode::Char('Y'), KeyModifiers::SHIFT)
),
Some(Action::CopyTableRow)
);
assert_eq!(
kb.dispatch(
KeybindingMode::InteractiveTable,
make_key_event(KeyCode::Char('r'), KeyModifiers::NONE)
),
Some(Action::CopyTableMarkdown)
);
}
#[test]
fn test_default_mouse_capture_toggle_binding() {
let mut kb = default_keybindings();
assert_eq!(
kb.dispatch(
KeybindingMode::Normal,
make_key_event(KeyCode::Char('M'), KeyModifiers::SHIFT)
),
Some(Action::ToggleMouseCapture)
);
}
#[test]
fn test_default_interactive_mode() {
let mut kb = default_keybindings();
assert_eq!(
kb.dispatch(
KeybindingMode::Interactive,
make_key_event(KeyCode::Esc, KeyModifiers::NONE)
),
Some(Action::ExitInteractiveMode)
);
assert_eq!(
kb.dispatch(
KeybindingMode::Interactive,
make_key_event(KeyCode::Tab, KeyModifiers::NONE)
),
Some(Action::InteractiveNextLink)
);
}
#[test]
fn test_all_modes_have_bindings() {
let kb = default_keybindings();
let modes = [
KeybindingMode::Normal,
KeybindingMode::Help,
KeybindingMode::ThemePicker,
KeybindingMode::Interactive,
KeybindingMode::InteractiveTable,
KeybindingMode::LinkFollow,
KeybindingMode::LinkSearch,
KeybindingMode::Search,
KeybindingMode::DocSearch,
KeybindingMode::CommandPalette,
KeybindingMode::ConfirmDialog,
KeybindingMode::CellEdit,
];
for mode in modes {
assert!(
kb.get_mode_keybinds(mode).is_some(),
"Mode {:?} has no bindings",
mode
);
assert!(
!kb.get_mode_keybinds(mode).unwrap().as_slice().is_empty(),
"Mode {:?} has empty bindings",
mode
);
}
}
}