feat(ui): vim mode (#1553)

* feat(config): add vim option to config

* feat(ui): simple vim mode

* fix(windows): windows sadly doesn't support the stuff

* feat(ui): blinking

* fix(merge)

* revert: reverts some debugging stuff

* feat(ui): changes the defaut to insert, don't know what should be the default

* feat(ui): implements some vim parity

* doc: adds this to the docs

* docs(keybindings): adds vim mode keybindsings to the list of keybindings

* refactor: rustfmt and remove the docs for pr in own repo

* refactor: use execute!

* Update atuin/src/command/client/search/interactive.rs

---------

Co-authored-by: Ellie Huxtable <ellie@elliehuxtable.com>
This commit is contained in:
YummyOreo 2024-01-13 09:15:49 -08:00 committed by GitHub
parent 4d41a741f0
commit a56085f059
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 4 deletions

View File

@ -125,6 +125,10 @@
# This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
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
#[stats]
# Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
#common_subcommands = [
@ -136,6 +140,6 @@ enter_accept = true
# "pnpm",
# "kubectl",
#]
#
#
# Set commands that should be totally stripped and ignored from stats
#common_prefix = ["sudo"]

View File

@ -201,6 +201,7 @@ pub struct Settings {
pub max_preview_height: u16,
pub show_help: bool,
pub exit_mode: ExitMode,
pub vim: bool,
pub word_jump_mode: WordJumpMode,
pub word_chars: String,
pub scroll_context_lines: usize,
@ -436,6 +437,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)?
.add_source(
Environment::with_prefix("atuin")
.prefix_separator("_")

View File

@ -5,6 +5,7 @@ use std::{
use atuin_common::utils;
use crossterm::{
cursor::SetCursorStyle,
event::{
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
KeyboardEnhancementFlags, MouseEvent, PopKeyboardEnhancementFlags,
@ -53,6 +54,12 @@ pub enum InputAction {
Redraw,
}
#[derive(PartialEq)]
enum VimMode {
Normal,
Insert,
}
#[allow(clippy::struct_field_names)]
pub struct State {
history_count: i64,
@ -62,6 +69,7 @@ pub struct State {
search_mode: SearchMode,
results_len: usize,
accept: bool,
vim_mode: VimMode,
tab_index: usize,
search: SearchState,
@ -141,6 +149,10 @@ 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 => {
let _ = execute!(stdout(), SetCursorStyle::SteadyBlock);
self.vim_mode = VimMode::Normal;
}
KeyCode::Esc => {
return match settings.exit_mode {
ExitMode::ReturnOriginal => InputAction::ReturnOriginal,
@ -294,6 +306,22 @@ impl State {
ExitMode::ReturnQuery => InputAction::ReturnQuery,
}
}
KeyCode::Char('j')
if settings.vim
&& self.vim_mode == VimMode::Normal
&& self.results_state.selected() == 0 =>
{
return match settings.exit_mode {
ExitMode::ReturnOriginal => InputAction::ReturnOriginal,
ExitMode::ReturnQuery => InputAction::ReturnQuery,
}
}
KeyCode::Char('k') if settings.vim && self.vim_mode == VimMode::Normal => {
self.scroll_up(1);
}
KeyCode::Char('j') if settings.vim && self.vim_mode == VimMode::Normal => {
self.scroll_down(1);
}
KeyCode::Down if !settings.invert => {
self.scroll_down(1);
}
@ -321,7 +349,13 @@ impl State {
KeyCode::Char('l') if ctrl => {
return InputAction::Redraw;
}
KeyCode::Char(c) => self.search.input.insert(c),
KeyCode::Char('i') if settings.vim && self.vim_mode == VimMode::Normal => {
let _ = execute!(stdout(), SetCursorStyle::BlinkingBlock);
self.vim_mode = VimMode::Insert;
}
KeyCode::Char(c) if !settings.vim || self.vim_mode == VimMode::Insert => {
self.search.input.insert(c);
}
KeyCode::PageDown if !settings.invert => {
let scroll_len = self.results_state.max_entries() - settings.scroll_context_lines;
self.scroll_down(scroll_len);
@ -669,6 +703,28 @@ struct Stdout {
}
impl Stdout {
#[cfg(target_os = "windows")]
pub fn new(inline_mode: bool) -> std::io::Result<Self> {
terminal::enable_raw_mode()?;
let mut stdout = stdout();
if !inline_mode {
execute!(stdout, terminal::EnterAlternateScreen)?;
}
execute!(
stdout,
event::EnableMouseCapture,
event::EnableBracketedPaste,
)?;
Ok(Self {
stdout,
inline_mode,
})
}
#[cfg(not(target_os = "windows"))]
pub fn new(inline_mode: bool) -> std::io::Result<Self> {
terminal::enable_raw_mode()?;
let mut stdout = stdout();
@ -695,6 +751,21 @@ impl Stdout {
}
impl Drop for Stdout {
#[cfg(target_os = "windows")]
fn drop(&mut self) {
if !self.inline_mode {
execute!(self.stdout, terminal::LeaveAlternateScreen).unwrap();
}
execute!(
self.stdout,
event::DisableMouseCapture,
event::DisableBracketedPaste,
)
.unwrap();
terminal::disable_raw_mode().unwrap();
}
#[cfg(not(target_os = "windows"))]
fn drop(&mut self) {
if !self.inline_mode {
execute!(self.stdout, terminal::LeaveAlternateScreen).unwrap();
@ -783,6 +854,7 @@ pub async fn history(
engine: engines::engine(search_mode),
results_len: 0,
accept: false,
vim_mode: VimMode::Normal,
};
let mut results = app.query_results(&mut db).await?;

View File

@ -18,7 +18,7 @@ filter_mode_shell_up_key_binding = "directory" # or global, host, directory, etc
## Disable up arrow
Our default up-arrow binding can be a bit contentious. Some people love it, some people hate it. Many people who found it a bit jarring at first have since come to love it, so give it a try!
Our default up-arrow binding can be a bit contentious. Some people love it, some people hate it. Many people who found it a bit jarring at first have since come to love it, so give it a try!
It becomes much more powerful if you consider binding a different filter mode to the up arrow. For example, on "up" Atuin can default to searching all history for the current directory only, while ctrl-r searches history globally. See the [config](https://atuin.sh/docs/config/#filter_mode_shell_up_key_binding) for more.
@ -159,4 +159,3 @@ $env.config = (
| page up | Scroll search results one page up |
| ⬇ (with no entry selected) | Return original or return query depending on settings |
| ⬇ | Select the next item on the list |