Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add emacs keymap mode #94

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@ pub fn set_global_render_config(config: RenderConfig) {
/// Default page size when displaying options to the user.
pub const DEFAULT_PAGE_SIZE: usize = 7;

/// Default value of vim mode.
pub const DEFAULT_VIM_MODE: bool = false;
#[derive(Debug, Clone, Copy, PartialEq)]
/// Specifies the keymap mode to use when navigating prompts.
pub enum KeymapMode {
/// No keybindings are enabled
None,
/// Vim keybindings (hjkl) are enabled
Vim,
/// Emacs keybindings (C-p, C-n, C-f, C-b) are enabled
Emacs,
}

/// Default value of keymap mode.
pub const DEFAULT_KEYMAP_MODE: KeymapMode = KeymapMode::None;
45 changes: 29 additions & 16 deletions src/prompts/dateselect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
};

use crate::{
config::get_configuration,
config::{get_configuration, KeymapMode},
date_utils::{get_current_date, get_month},
error::{InquireError, InquireResult},
formatter::{self, DateFormatter},
Expand Down Expand Up @@ -76,9 +76,10 @@ pub struct DateSelect<'a> {
/// Help message to be presented to the user.
pub help_message: Option<&'a str>,

/// Whether vim mode is enabled. When enabled, the user can
/// navigate through the options using hjkl.
pub vim_mode: bool,
/// Whether a keymap mode is enabled.
/// Vim mode: user can navigate through the options using hjkl.
/// Emacs mode: user can navigate through the options using C-p, C-n, C-f, C-b.
pub keymap_mode: KeymapMode,

/// Function that formats the user input and presents it to the user as the final rendering of the prompt.
pub formatter: DateFormatter<'a>,
Expand Down Expand Up @@ -106,8 +107,8 @@ impl<'a> DateSelect<'a> {
/// Default formatter, set to [DEFAULT_DATE_FORMATTER](crate::formatter::DEFAULT_DATE_FORMATTER)
pub const DEFAULT_FORMATTER: DateFormatter<'a> = formatter::DEFAULT_DATE_FORMATTER;

/// Default value of vim mode. It is true because there is no typing functionality to be lost here.
pub const DEFAULT_VIM_MODE: bool = true;
/// Default value of keymap mode. It is Vim by default because there is no typing functionality to be lost here.
pub const DEFAULT_KEYMAP_MODE: KeymapMode = KeymapMode::Vim;

/// Default help message.
pub const DEFAULT_HELP_MESSAGE: Option<&'a str> =
Expand All @@ -133,7 +134,7 @@ impl<'a> DateSelect<'a> {
min_date: Self::DEFAULT_MIN_DATE,
max_date: Self::DEFAULT_MAX_DATE,
help_message: Self::DEFAULT_HELP_MESSAGE,
vim_mode: Self::DEFAULT_VIM_MODE,
keymap_mode: Self::DEFAULT_KEYMAP_MODE,
formatter: Self::DEFAULT_FORMATTER,
validators: Self::DEFAULT_VALIDATORS,
week_start: Self::DEFAULT_WEEK_START,
Expand Down Expand Up @@ -218,9 +219,9 @@ impl<'a> DateSelect<'a> {
self
}

/// Enables or disables vim_mode.
pub fn with_vim_mode(mut self, vim_mode: bool) -> Self {
self.vim_mode = vim_mode;
/// Enables or disables keymap_mode.
pub fn with_keymap_mode(mut self, keymap_mode: KeymapMode) -> Self {
self.keymap_mode = keymap_mode;
self
}

Expand Down Expand Up @@ -283,7 +284,7 @@ struct DateSelectPrompt<'a> {
min_date: Option<NaiveDate>,
max_date: Option<NaiveDate>,
help_message: Option<&'a str>,
vim_mode: bool,
keymap_mode: KeymapMode,
formatter: DateFormatter<'a>,
validators: Vec<Box<dyn DateValidator>>,
error: Option<ErrorMessage>,
Expand Down Expand Up @@ -313,7 +314,7 @@ impl<'a> DateSelectPrompt<'a> {
max_date: so.max_date,
week_start: so.week_start,
help_message: so.help_message,
vim_mode: so.vim_mode,
keymap_mode: so.keymap_mode,
formatter: so.formatter,
validators: so.validators,
error: None,
Expand Down Expand Up @@ -359,22 +360,34 @@ impl<'a> DateSelectPrompt<'a> {
fn on_change(&mut self, key: Key) {
match key {
Key::Up(KeyModifiers::NONE) => self.shift_date(Duration::weeks(-1)),
Key::Char('k', KeyModifiers::NONE) if self.vim_mode => {
Key::Char('k', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.shift_date(Duration::weeks(-1))
}
Key::Char('p', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.shift_date(Duration::weeks(-1))
}

Key::Down(KeyModifiers::NONE) | Key::Tab => self.shift_date(Duration::weeks(1)),
Key::Char('j', KeyModifiers::NONE) if self.vim_mode => {
Key::Char('j', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.shift_date(Duration::weeks(1))
}
Key::Char('n', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.shift_date(Duration::weeks(1))
}

Key::Left(KeyModifiers::NONE) => self.shift_date(Duration::days(-1)),
Key::Char('h', KeyModifiers::NONE) if self.vim_mode => {
Key::Char('h', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.shift_date(Duration::days(-1))
}
Key::Char('b', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.shift_date(Duration::days(-1))
}

Key::Right(KeyModifiers::NONE) => self.shift_date(Duration::days(1)),
Key::Char('l', KeyModifiers::NONE) if self.vim_mode => {
Key::Char('l', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.shift_date(Duration::days(1))
}
Key::Char('f', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.shift_date(Duration::days(1))
}

Expand Down
1 change: 1 addition & 0 deletions src/prompts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod password;
mod select;
mod text;

pub use crate::config::KeymapMode;
pub use confirm::Confirm;
pub use custom_type::CustomType;
#[cfg(feature = "date")]
Expand Down
39 changes: 25 additions & 14 deletions src/prompts/multiselect.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::BTreeSet, fmt::Display};

use crate::{
config::{self, get_configuration},
config::{self, get_configuration, KeymapMode},
error::{InquireError, InquireResult},
formatter::MultiOptionFormatter,
input::Input,
Expand Down Expand Up @@ -60,9 +60,10 @@ pub struct MultiSelect<'a, T> {
/// Page size of the options displayed to the user.
pub page_size: usize,

/// Whether vim mode is enabled. When enabled, the user can
/// navigate through the options using hjkl.
pub vim_mode: bool,
/// Whether a keymap mode is enabled.
/// Vim mode: user can navigate through the options using hjkl.
/// Emacs mode: user can navigate through the options using C-p, C-n, C-f, C-b.
pub keymap_mode: KeymapMode,

/// Starting cursor index of the selection.
pub starting_cursor: usize,
Expand Down Expand Up @@ -156,8 +157,8 @@ where
/// Default page size, equal to the global default page size [config::DEFAULT_PAGE_SIZE]
pub const DEFAULT_PAGE_SIZE: usize = config::DEFAULT_PAGE_SIZE;

/// Default value of vim mode, equal to the global default value [config::DEFAULT_PAGE_SIZE]
pub const DEFAULT_VIM_MODE: bool = config::DEFAULT_VIM_MODE;
/// Default value of keymap mode, equal to the global default value [config::DEFAULT_PAGE_SIZE]
pub const DEFAULT_KEYMAP_MODE: KeymapMode = config::DEFAULT_KEYMAP_MODE;

/// Default starting cursor index.
pub const DEFAULT_STARTING_CURSOR: usize = 0;
Expand All @@ -177,7 +178,7 @@ where
default: None,
help_message: Self::DEFAULT_HELP_MESSAGE,
page_size: Self::DEFAULT_PAGE_SIZE,
vim_mode: Self::DEFAULT_VIM_MODE,
keymap_mode: Self::DEFAULT_KEYMAP_MODE,
starting_cursor: Self::DEFAULT_STARTING_CURSOR,
keep_filter: Self::DEFAULT_KEEP_FILTER,
filter: Self::DEFAULT_FILTER,
Expand Down Expand Up @@ -205,9 +206,9 @@ where
self
}

/// Enables or disables vim_mode.
pub fn with_vim_mode(mut self, vim_mode: bool) -> Self {
self.vim_mode = vim_mode;
/// Enables or disables keymap_mode.
pub fn with_keymap_mode(mut self, keymap_mode: KeymapMode) -> Self {
self.keymap_mode = keymap_mode;
self
}

Expand Down Expand Up @@ -339,7 +340,7 @@ struct MultiSelectPrompt<'a, T> {
options: Vec<T>,
string_options: Vec<String>,
help_message: Option<&'a str>,
vim_mode: bool,
keymap_mode: KeymapMode,
cursor_index: usize,
checked: BTreeSet<usize>,
page_size: usize,
Expand Down Expand Up @@ -386,7 +387,7 @@ where
string_options,
filtered_options,
help_message: mso.help_message,
vim_mode: mso.vim_mode,
keymap_mode: mso.keymap_mode,
cursor_index: mso.starting_cursor,
page_size: mso.page_size,
keep_filter: mso.keep_filter,
Expand Down Expand Up @@ -457,12 +458,22 @@ where
fn on_change(&mut self, key: Key) {
match key {
Key::Up(KeyModifiers::NONE) => self.move_cursor_up(1, true),
Key::Char('k', KeyModifiers::NONE) if self.vim_mode => self.move_cursor_up(1, true),
Key::Char('k', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.move_cursor_up(1, true)
}
Key::Char('p', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.move_cursor_up(1, true)
}
Key::PageUp => self.move_cursor_up(self.page_size, false),
Key::Home => self.move_cursor_up(usize::MAX, false),

Key::Down(KeyModifiers::NONE) => self.move_cursor_down(1, true),
Key::Char('j', KeyModifiers::NONE) if self.vim_mode => self.move_cursor_down(1, true),
Key::Char('j', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.move_cursor_down(1, true)
}
Key::Char('n', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.move_cursor_down(1, true)
}
Key::PageDown => self.move_cursor_down(self.page_size, false),
Key::End => self.move_cursor_down(usize::MAX, false),

Expand Down
39 changes: 25 additions & 14 deletions src/prompts/select.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fmt::Display;

use crate::{
config::{self, get_configuration},
config::{self, get_configuration, KeymapMode},
error::{InquireError, InquireResult},
formatter::OptionFormatter,
input::Input,
Expand Down Expand Up @@ -67,9 +67,10 @@ pub struct Select<'a, T> {
/// Page size of the options displayed to the user.
pub page_size: usize,

/// Whether vim mode is enabled. When enabled, the user can
/// navigate through the options using hjkl.
pub vim_mode: bool,
/// Whether a keymap mode is enabled.
/// Vim mode: user can navigate through the options using hjkl.
/// Emacs mode: user can navigate through the options using C-p, C-n, C-f, C-b.
pub keymap_mode: KeymapMode,

/// Starting cursor index of the selection.
pub starting_cursor: usize,
Expand Down Expand Up @@ -143,8 +144,8 @@ where
/// Default page size.
pub const DEFAULT_PAGE_SIZE: usize = config::DEFAULT_PAGE_SIZE;

/// Default value of vim mode.
pub const DEFAULT_VIM_MODE: bool = config::DEFAULT_VIM_MODE;
/// Default value of keymap mode.
pub const DEFAULT_KEYMAP_MODE: KeymapMode = config::DEFAULT_KEYMAP_MODE;

/// Default starting cursor index.
pub const DEFAULT_STARTING_CURSOR: usize = 0;
Expand All @@ -160,7 +161,7 @@ where
options,
help_message: Self::DEFAULT_HELP_MESSAGE,
page_size: Self::DEFAULT_PAGE_SIZE,
vim_mode: Self::DEFAULT_VIM_MODE,
keymap_mode: Self::DEFAULT_KEYMAP_MODE,
starting_cursor: Self::DEFAULT_STARTING_CURSOR,
filter: Self::DEFAULT_FILTER,
formatter: Self::DEFAULT_FORMATTER,
Expand All @@ -186,9 +187,9 @@ where
self
}

/// Enables or disables vim_mode.
pub fn with_vim_mode(mut self, vim_mode: bool) -> Self {
self.vim_mode = vim_mode;
/// Enables or disables keymap_mode.
pub fn with_keymap_mode(mut self, keymap_mode: KeymapMode) -> Self {
self.keymap_mode = keymap_mode;
self
}

Expand Down Expand Up @@ -273,7 +274,7 @@ struct SelectPrompt<'a, T> {
string_options: Vec<String>,
filtered_options: Vec<usize>,
help_message: Option<&'a str>,
vim_mode: bool,
keymap_mode: KeymapMode,
cursor_index: usize,
page_size: usize,
input: Input,
Expand Down Expand Up @@ -309,7 +310,7 @@ where
string_options,
filtered_options,
help_message: so.help_message,
vim_mode: so.vim_mode,
keymap_mode: so.keymap_mode,
cursor_index: so.starting_cursor,
page_size: so.page_size,
input: Input::new(),
Expand Down Expand Up @@ -359,12 +360,22 @@ where
fn on_change(&mut self, key: Key) {
match key {
Key::Up(KeyModifiers::NONE) => self.move_cursor_up(1, true),
Key::Char('k', KeyModifiers::NONE) if self.vim_mode => self.move_cursor_up(1, true),
Key::Char('k', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.move_cursor_up(1, true)
}
Key::Char('p', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.move_cursor_up(1, true)
}
Key::PageUp => self.move_cursor_up(self.page_size, false),
Key::Home => self.move_cursor_up(usize::MAX, false),

Key::Down(KeyModifiers::NONE) => self.move_cursor_down(1, true),
Key::Char('j', KeyModifiers::NONE) if self.vim_mode => self.move_cursor_down(1, true),
Key::Char('j', KeyModifiers::NONE) if self.keymap_mode == KeymapMode::Vim => {
self.move_cursor_down(1, true)
}
Key::Char('n', KeyModifiers::CONTROL) if self.keymap_mode == KeymapMode::Emacs => {
self.move_cursor_down(1, true)
}
Key::PageDown => self.move_cursor_down(self.page_size, false),
Key::End => self.move_cursor_down(usize::MAX, false),

Expand Down