mirror of
https://github.com/atuinsh/atuin.git
synced 2025-01-24 15:20:32 +01:00
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"
This commit is contained in:
parent
a2578c4521
commit
6bff8c8e1a
@ -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
|
||||
|
@ -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("_")
|
||||
|
@ -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}");
|
||||
|
@ -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?;
|
||||
|
@ -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}");
|
||||
|
@ -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"'
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user