mirror of
https://github.com/atuinsh/atuin.git
synced 2025-06-25 12:21:55 +02:00
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:
parent
4d41a741f0
commit
a56085f059
@ -125,6 +125,10 @@
|
|||||||
# This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
|
# This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
|
||||||
enter_accept = true
|
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]
|
#[stats]
|
||||||
# Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
|
# Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
|
||||||
#common_subcommands = [
|
#common_subcommands = [
|
||||||
@ -136,6 +140,6 @@ enter_accept = true
|
|||||||
# "pnpm",
|
# "pnpm",
|
||||||
# "kubectl",
|
# "kubectl",
|
||||||
#]
|
#]
|
||||||
#
|
#
|
||||||
# Set commands that should be totally stripped and ignored from stats
|
# Set commands that should be totally stripped and ignored from stats
|
||||||
#common_prefix = ["sudo"]
|
#common_prefix = ["sudo"]
|
||||||
|
@ -201,6 +201,7 @@ pub struct Settings {
|
|||||||
pub max_preview_height: u16,
|
pub max_preview_height: u16,
|
||||||
pub show_help: bool,
|
pub show_help: bool,
|
||||||
pub exit_mode: ExitMode,
|
pub exit_mode: ExitMode,
|
||||||
|
pub vim: bool,
|
||||||
pub word_jump_mode: WordJumpMode,
|
pub word_jump_mode: WordJumpMode,
|
||||||
pub word_chars: String,
|
pub word_chars: String,
|
||||||
pub scroll_context_lines: usize,
|
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.
|
// New users will get the new default, that is more similar to what they are used to.
|
||||||
.set_default("enter_accept", false)?
|
.set_default("enter_accept", false)?
|
||||||
.set_default("sync.records", false)?
|
.set_default("sync.records", false)?
|
||||||
|
.set_default("vim", false)?
|
||||||
.add_source(
|
.add_source(
|
||||||
Environment::with_prefix("atuin")
|
Environment::with_prefix("atuin")
|
||||||
.prefix_separator("_")
|
.prefix_separator("_")
|
||||||
|
@ -5,6 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use atuin_common::utils;
|
use atuin_common::utils;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
|
cursor::SetCursorStyle,
|
||||||
event::{
|
event::{
|
||||||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
|
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
|
||||||
KeyboardEnhancementFlags, MouseEvent, PopKeyboardEnhancementFlags,
|
KeyboardEnhancementFlags, MouseEvent, PopKeyboardEnhancementFlags,
|
||||||
@ -53,6 +54,12 @@ pub enum InputAction {
|
|||||||
Redraw,
|
Redraw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum VimMode {
|
||||||
|
Normal,
|
||||||
|
Insert,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::struct_field_names)]
|
#[allow(clippy::struct_field_names)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
history_count: i64,
|
history_count: i64,
|
||||||
@ -62,6 +69,7 @@ pub struct State {
|
|||||||
search_mode: SearchMode,
|
search_mode: SearchMode,
|
||||||
results_len: usize,
|
results_len: usize,
|
||||||
accept: bool,
|
accept: bool,
|
||||||
|
vim_mode: VimMode,
|
||||||
tab_index: usize,
|
tab_index: usize,
|
||||||
|
|
||||||
search: SearchState,
|
search: SearchState,
|
||||||
@ -141,6 +149,10 @@ impl State {
|
|||||||
// core input handling, common for all tabs
|
// core input handling, common for all tabs
|
||||||
match input.code {
|
match input.code {
|
||||||
KeyCode::Char('c' | 'g') if ctrl => return InputAction::ReturnOriginal,
|
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 => {
|
KeyCode::Esc => {
|
||||||
return match settings.exit_mode {
|
return match settings.exit_mode {
|
||||||
ExitMode::ReturnOriginal => InputAction::ReturnOriginal,
|
ExitMode::ReturnOriginal => InputAction::ReturnOriginal,
|
||||||
@ -294,6 +306,22 @@ impl State {
|
|||||||
ExitMode::ReturnQuery => InputAction::ReturnQuery,
|
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 => {
|
KeyCode::Down if !settings.invert => {
|
||||||
self.scroll_down(1);
|
self.scroll_down(1);
|
||||||
}
|
}
|
||||||
@ -321,7 +349,13 @@ impl State {
|
|||||||
KeyCode::Char('l') if ctrl => {
|
KeyCode::Char('l') if ctrl => {
|
||||||
return InputAction::Redraw;
|
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 => {
|
KeyCode::PageDown if !settings.invert => {
|
||||||
let scroll_len = self.results_state.max_entries() - settings.scroll_context_lines;
|
let scroll_len = self.results_state.max_entries() - settings.scroll_context_lines;
|
||||||
self.scroll_down(scroll_len);
|
self.scroll_down(scroll_len);
|
||||||
@ -669,6 +703,28 @@ struct Stdout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub fn new(inline_mode: bool) -> std::io::Result<Self> {
|
||||||
terminal::enable_raw_mode()?;
|
terminal::enable_raw_mode()?;
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
@ -695,6 +751,21 @@ impl Stdout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for 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) {
|
fn drop(&mut self) {
|
||||||
if !self.inline_mode {
|
if !self.inline_mode {
|
||||||
execute!(self.stdout, terminal::LeaveAlternateScreen).unwrap();
|
execute!(self.stdout, terminal::LeaveAlternateScreen).unwrap();
|
||||||
@ -783,6 +854,7 @@ pub async fn history(
|
|||||||
engine: engines::engine(search_mode),
|
engine: engines::engine(search_mode),
|
||||||
results_len: 0,
|
results_len: 0,
|
||||||
accept: false,
|
accept: false,
|
||||||
|
vim_mode: VimMode::Normal,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut results = app.query_results(&mut db).await?;
|
let mut results = app.query_results(&mut db).await?;
|
||||||
|
@ -18,7 +18,7 @@ filter_mode_shell_up_key_binding = "directory" # or global, host, directory, etc
|
|||||||
|
|
||||||
## Disable up arrow
|
## 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.
|
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 |
|
| page up | Scroll search results one page up |
|
||||||
| ⬇ (with no entry selected) | Return original or return query depending on settings |
|
| ⬇ (with no entry selected) | Return original or return query depending on settings |
|
||||||
| ⬇ | Select the next item on the list |
|
| ⬇ | Select the next item on the list |
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user