summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKoichi Murase <myoga.murase@gmail.com>2024-01-16 22:35:10 +0900
committerGitHub <noreply@github.com>2024-01-16 13:35:10 +0000
commit6bff8c8e1ad3a230f3cd8f5d7078ed2af3f43463 (patch)
treefa9ca6ee52cb5c100716b764b60453d157e681d8
parenta2578c4521d4615d8265744ab51a1cc4f291605e (diff)
feat(search): introduce keymap-dependent vim-mode (#1570)
* feat(search): introduce keymap-dependent vim-mode * fix(zsh): provide widgets with specific keymaps * fix(settings): unify "vim" and "keymap_mode"
-rw-r--r--atuin-client/config.toml9
-rw-r--r--atuin-client/src/settings.rs30
-rw-r--r--atuin/src/command/client/search.rs13
-rw-r--r--atuin/src/command/client/search/interactive.rs35
-rw-r--r--atuin/src/command/init.rs14
-rw-r--r--atuin/src/shell/atuin.bash24
-rw-r--r--atuin/src/shell/atuin.fish15
-rw-r--r--atuin/src/shell/atuin.zsh18
8 files changed, 112 insertions, 46 deletions
diff --git a/atuin-client/config.toml b/atuin-client/config.toml
index 29581d1fa..24366b60e 100644
--- a/atuin-client/config.toml
+++ b/atuin-client/config.toml
@@ -126,8 +126,13 @@
enter_accept = true
-## Defaults to false. If enabled you may use 'j' and 'k' to navigate the history list and 'i' to enter Insert mode.
-# vim = false
+## Defaults to "emacs". This specifies the keymap on the startup of `atuin
+## search`. If this is set to "auto", the startup keymap mode in the Atuin
+## search is automatically selected based on the shell's keymap where the
+## keybinding is defined. If this is set to "emacs", "vim-insert", or
+## "vim-normal", the startup keymap mode in the Atuin search is forced to be
+## the specified one.
+# keymap_mode = "auto"
#[stats]
# Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs
index ea01961c7..36bbd826b 100644
--- a/atuin-client/src/settings.rs
+++ b/atuin-client/src/settings.rs
@@ -143,6 +143,32 @@ pub enum WordJumpMode {
Subl,
}
+#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum)]
+pub enum KeymapMode {
+ #[serde(rename = "emacs")]
+ Emacs,
+
+ #[serde(rename = "vim-normal")]
+ VimNormal,
+
+ #[serde(rename = "vim-insert")]
+ VimInsert,
+
+ #[serde(rename = "auto")]
+ Auto,
+}
+
+impl KeymapMode {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ KeymapMode::Emacs => "EMACS",
+ KeymapMode::VimNormal => "VIMNORMAL",
+ KeymapMode::VimInsert => "VIMINSERT",
+ KeymapMode::Auto => "AUTO",
+ }
+ }
+}
+
#[derive(Clone, Debug, Deserialize)]
pub struct Stats {
#[serde(default = "Stats::common_prefix_default")]
@@ -201,7 +227,7 @@ pub struct Settings {
pub max_preview_height: u16,
pub show_help: bool,
pub exit_mode: ExitMode,
- pub vim: bool,
+ pub keymap_mode: KeymapMode,
pub word_jump_mode: WordJumpMode,
pub word_chars: String,
pub scroll_context_lines: usize,
@@ -437,7 +463,7 @@ impl Settings {
// New users will get the new default, that is more similar to what they are used to.
.set_default("enter_accept", false)?
.set_default("sync.records", false)?
- .set_default("vim", false)?
+ .set_default("keymap_mode", "emacs")?
.add_source(
Environment::with_prefix("atuin")
.prefix_separator("_")
diff --git a/atuin/src/command/client/search.rs b/atuin/src/command/client/search.rs
index 0875f3ad3..726da3489 100644
--- a/atuin/src/command/client/search.rs
+++ b/atuin/src/command/client/search.rs
@@ -6,7 +6,7 @@ use atuin_client::{
database::Database,
database::{current_context, OptFilters},
history::History,
- settings::{FilterMode, SearchMode, Settings},
+ settings::{FilterMode, KeymapMode, SearchMode, Settings},
};
use super::history::ListMode;
@@ -71,6 +71,10 @@ pub struct Cmd {
#[arg(long = "shell-up-key-binding", hide = true)]
shell_up_key_binding: bool,
+ /// Notify the keymap at the shell's side
+ #[arg(long = "keymap-mode", default_value = "auto")]
+ keymap_mode: KeymapMode,
+
/// Use human-readable formatting for time
#[arg(long)]
human: bool,
@@ -142,6 +146,13 @@ impl Cmd {
settings.shell_up_key_binding = self.shell_up_key_binding;
+ // `keymap_mode` specified in config.toml overrides the `--keymap-mode`
+ // option specified in the keybindings.
+ settings.keymap_mode = match settings.keymap_mode {
+ KeymapMode::Auto => self.keymap_mode,
+ value => value,
+ };
+
if self.interactive {
let item = interactive::history(&self.query, settings, db).await?;
eprintln!("{item}");
diff --git a/atuin/src/command/client/search/interactive.rs b/atuin/src/command/client/search/interactive.rs
index b47e55554..c838e8731 100644
--- a/atuin/src/command/client/search/interactive.rs
+++ b/atuin/src/command/client/search/interactive.rs
@@ -21,7 +21,7 @@ use unicode_width::UnicodeWidthStr;
use atuin_client::{
database::{current_context, Database},
history::{History, HistoryStats},
- settings::{ExitMode, FilterMode, SearchMode, Settings},
+ settings::{ExitMode, FilterMode, KeymapMode, SearchMode, Settings},
};
use super::{
@@ -54,12 +54,6 @@ pub enum InputAction {
Redraw,
}
-#[derive(PartialEq)]
-enum VimMode {
- Normal,
- Insert,
-}
-
#[allow(clippy::struct_field_names)]
pub struct State {
history_count: i64,
@@ -69,7 +63,7 @@ pub struct State {
search_mode: SearchMode,
results_len: usize,
accept: bool,
- vim_mode: VimMode,
+ keymap_mode: KeymapMode,
tab_index: usize,
search: SearchState,
@@ -159,9 +153,9 @@ impl State {
// core input handling, common for all tabs
match input.code {
KeyCode::Char('c' | 'g') if ctrl => return InputAction::ReturnOriginal,
- KeyCode::Esc if settings.vim && self.vim_mode == VimMode::Insert => {
+ KeyCode::Esc if self.keymap_mode == KeymapMode::VimInsert => {
let _ = execute!(stdout(), SetCursorStyle::SteadyBlock);
- self.vim_mode = VimMode::Normal;
+ self.keymap_mode = KeymapMode::VimNormal;
return InputAction::Continue;
}
KeyCode::Esc => {
@@ -311,8 +305,7 @@ impl State {
KeyCode::Char('j')
if !ctrl
&& !settings.invert
- && settings.vim
- && self.vim_mode == VimMode::Normal
+ && self.keymap_mode == KeymapMode::VimNormal
&& self.results_state.selected() == 0 =>
{
do_exit!();
@@ -320,16 +313,15 @@ impl State {
KeyCode::Char('k')
if !ctrl
&& settings.invert
- && settings.vim
- && self.vim_mode == VimMode::Normal
+ && self.keymap_mode == KeymapMode::VimNormal
&& self.results_state.selected() == 0 =>
{
do_exit!();
}
- KeyCode::Char('k') if !ctrl && settings.vim && self.vim_mode == VimMode::Normal => {
+ KeyCode::Char('k') if !ctrl && self.keymap_mode == KeymapMode::VimNormal => {
self.scroll_up(1);
}
- KeyCode::Char('j') if !ctrl && settings.vim && self.vim_mode == VimMode::Normal => {
+ KeyCode::Char('j') if !ctrl && self.keymap_mode == KeymapMode::VimNormal => {
self.scroll_down(1);
}
KeyCode::Down if !settings.invert => {
@@ -359,11 +351,11 @@ impl State {
KeyCode::Char('l') if ctrl => {
return InputAction::Redraw;
}
- KeyCode::Char('i') if settings.vim && self.vim_mode == VimMode::Normal => {
+ KeyCode::Char('i') if self.keymap_mode == KeymapMode::VimNormal => {
let _ = execute!(stdout(), SetCursorStyle::BlinkingBlock);
- self.vim_mode = VimMode::Insert;
+ self.keymap_mode = KeymapMode::VimInsert;
}
- KeyCode::Char(c) if !settings.vim || self.vim_mode == VimMode::Insert => {
+ KeyCode::Char(c) if self.keymap_mode != KeymapMode::VimNormal => {
self.search.input.insert(c);
}
KeyCode::PageDown if !settings.invert => {
@@ -832,7 +824,10 @@ pub async fn history(
engine: engines::engine(search_mode),
results_len: 0,
accept: false,
- vim_mode: VimMode::Normal,
+ keymap_mode: match settings.keymap_mode {
+ KeymapMode::Auto => KeymapMode::Emacs,
+ value => value,
+ },
};
let mut results = app.query_results(&mut db).await?;
diff --git a/atuin/src/command/init.rs b/atuin/src/command/init.rs
index 4eae98d37..ba5011813 100644
--- a/atuin/src/command/init.rs
+++ b/atuin/src/command/init.rs
@@ -33,16 +33,16 @@ impl Cmd {
if std::env::var("ATUIN_NOBIND").is_err() {
const BIND_CTRL_R: &str = r"bindkey -M emacs '^r' _atuin_search_widget
-bindkey -M vicmd '^r' _atuin_search_widget
-bindkey -M viins '^r' _atuin_search_widget";
+bindkey -M vicmd '^r' _atuin_search_vicmd_widget
+bindkey -M viins '^r' _atuin_search_viins_widget";
const BIND_UP_ARROW: &str = r"bindkey -M emacs '^[[A' _atuin_up_search_widget
-bindkey -M vicmd '^[[A' _atuin_up_search_widget
-bindkey -M viins '^[[A' _atuin_up_search_widget
+bindkey -M vicmd '^[[A' _atuin_up_search_vicmd_widget
+bindkey -M viins '^[[A' _atuin_up_search_viins_widget
bindkey -M emacs '^[OA' _atuin_up_search_widget
-bindkey -M vicmd '^[OA' _atuin_up_search_widget
-bindkey -M viins '^[OA' _atuin_up_search_widget
-bindkey -M vicmd 'k' _atuin_up_search_widget";
+bindkey -M vicmd '^[OA' _atuin_up_search_vicmd_widget
+bindkey -M viins '^[OA' _atuin_up_search_viins_widget
+bindkey -M vicmd 'k' _atuin_up_search_vicmd_widget";
if !self.disable_ctrl_r {
println!("{BIND_CTRL_R}");
diff --git a/atuin/src/shell/atuin.bash b/atuin/src/shell/atuin.bash
index 933416e67..490b7f715 100644
--- a/atuin/src/shell/atuin.bash
+++ b/atuin/src/shell/atuin.bash
@@ -227,33 +227,33 @@ if [[ $__atuin_bind_ctrl_r == true ]]; then
# Note: We do not overwrite [C-r] in the vi-command keymap for Bash because
# we do not want to overwrite "redo", which is already bound to [C-r] in
# the vi_nmap keymap in ble.sh.
- bind -m emacs -x '"\C-r": __atuin_history'
- bind -m vi-insert -x '"\C-r": __atuin_history'
+ bind -m emacs -x '"\C-r": __atuin_history --keymap-mode=emacs'
+ bind -m vi-insert -x '"\C-r": __atuin_history --keymap-mode=vim-insert'
fi
# shellcheck disable=SC2154
if [[ $__atuin_bind_up_arrow == true ]]; then
if ((BASH_VERSINFO[0] > 4 || BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 3)); then
- bind -m emacs -x '"\e[A": __atuin_history --shell-up-key-binding'
- bind -m emacs -x '"\eOA": __atuin_history --shell-up-key-binding'
- bind -m vi-insert -x '"\e[A": __atuin_history --shell-up-key-binding'
- bind -m vi-insert -x '"\eOA": __atuin_history --shell-up-key-binding'
- bind -m vi-command -x '"\e[A": __atuin_history --shell-up-key-binding'
- bind -m vi-command -x '"\eOA": __atuin_history --shell-up-key-binding'
- bind -m vi-command -x '"k": __atuin_history --shell-up-key-binding'
+ bind -m emacs -x '"\e[A": __atuin_history --shell-up-key-binding --keymap-mode=emacs'
+ bind -m emacs -x '"\eOA": __atuin_history --shell-up-key-binding --keymap-mode=emacs'
+ bind -m vi-insert -x '"\e[A": __atuin_history --shell-up-key-binding --keymap-mode=vim-insert'
+ bind -m vi-insert -x '"\eOA": __atuin_history --shell-up-key-binding --keymap-mode=vim-insert'
+ bind -m vi-command -x '"\e[A": __atuin_history --shell-up-key-binding --keymap-mode=vim-normal'
+ bind -m vi-command -x '"\eOA": __atuin_history --shell-up-key-binding --keymap-mode=vim-normal'
+ bind -m vi-command -x '"k": __atuin_history --shell-up-key-binding --keymap-mode=vim-normal'
else
# In bash < 4.3, "bind -x" cannot bind a shell command to a keyseq
# having more than two bytes. To work around this, we first translate
# the keyseqs to the two-byte sequence \C-x\C-p (which is not used by
# default) using string macros and run the shell command through the
# keybinding to \C-x\C-p.
- bind -m emacs -x '"\C-x\C-p": __atuin_history --shell-up-key-binding'
+ bind -m emacs -x '"\C-x\C-p": __atuin_history --shell-up-key-binding --keymap-mode=emacs'
bind -m emacs '"\e[A": "\C-x\C-p"'
bind -m emacs '"\eOA": "\C-x\C-p"'
- bind -m vi-insert -x '"\C-x\C-p": __atuin_history --shell-up-key-binding'
+ bind -m vi-insert -x '"\C-x\C-p": __atuin_history --shell-up-key-binding --keymap-mode=vim-insert'
bind -m vi-insert -x '"\e[A": "\C-x\C-p"'
bind -m vi-insert -x '"\eOA": "\C-x\C-p"'
- bind -m vi-command -x '"\C-x\C-p": __atuin_history --shell-up-key-binding'
+ bind -m vi-command -x '"\C-x\C-p": __atuin_history --shell-up-key-binding --keymap-mode=vim-normal'
bind -m vi-command -x '"\e[A": "\C-x\C-p"'
bind -m vi-command -x '"\eOA": "\C-x\C-p"'
bind -m vi-command -x '"k": "\C-x\C-p"'
diff --git a/atuin/src/shell/atuin.fish b/atuin/src/shell/atuin.fish
index ee4100585..b10d70ec8 100644
--- a/atuin/src/shell/atuin.fish
+++ b/atuin/src/shell/atuin.fish
@@ -19,10 +19,23 @@ function _atuin_postexec --on-event fish_postexec
end
function _atuin_search
+ set -l keymap_mode
+ switch $fish_key_bindings
+ case fish_vi_key_bindings
+ switch $fish_bind_mode
+ case default
+ set keymap_mode vim-normal
+ case insert
+ set keymap_mode vim-insert
+ end
+ case '*'
+ set keymap_mode emacs
+ end
+
# In fish 3.4 and above we can use `"$(some command)"` to keep multiple lines separate;
# but to support fish 3.3 we need to use `(some command | string collect)`.
# https://fishshell.com/docs/current/relnotes.html#id24 (fish 3.4 "Notable improvements and fixes")
- set -l ATUIN_H (ATUIN_SHELL_FISH=t ATUIN_LOG=error atuin search $argv -i -- (commandline -b) 3>&1 1>&2 2>&3 | string collect)
+ set -l ATUIN_H (ATUIN_SHELL_FISH=t ATUIN_LOG=error atuin search --keymap-mode=$keymap_mode $argv -i -- (commandline -b) 3>&1 1>&2 2>&3 | string collect)
if test -n "$ATUIN_H"
if string match --quiet '__atuin_accept__:*' "$ATUIN_H"
diff --git a/atuin/src/shell/atuin.zsh b/atuin/src/shell/atuin.zsh
index 85aba1829..152d674f2 100644
--- a/atuin/src/shell/atuin.zsh
+++ b/atuin/src/shell/atuin.zsh
@@ -71,18 +71,34 @@ _atuin_search() {
fi
fi
}
+_atuin_search_vicmd() {
+ _atuin_search --keymap-mode=vim-normal
+}
+_atuin_search_viins() {
+ _atuin_search --keymap-mode=vim-insert
+}
_atuin_up_search() {
# Only trigger if the buffer is a single line
if [[ ! $BUFFER == *$'\n'* ]]; then
- _atuin_search --shell-up-key-binding
+ _atuin_search --shell-up-key-binding "$@"
else
zle up-line
fi
}
+_atuin_up_search_vicmd() {
+ _atuin_up_search --keymap-mode=vim-normal
+}
+_atuin_up_search_viins() {
+ _atuin_up_search --keymap-mode=vim-insert
+}
add-zsh-hook preexec _atuin_preexec
add-zsh-hook precmd _atuin_precmd
zle -N _atuin_search_widget _atuin_search
+zle -N _atuin_search_vicmd_widget _atuin_search_vicmd
+zle -N _atuin_search_viins_widget _atuin_search_viins
zle -N _atuin_up_search_widget _atuin_up_search
+zle -N _atuin_up_search_vicmd_widget _atuin_up_search_vicmd
+zle -N _atuin_up_search_viins_widget _atuin_up_search_viins