summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYummyOreo <bobgim20@gmail.com>2024-01-13 09:15:49 -0800
committerGitHub <noreply@github.com>2024-01-13 17:15:49 +0000
commita56085f059b2444812353e715e5a4b93ea75b018 (patch)
tree6384c32f9ab242bf3fa7bfece81d29faa4a735d2
parent4d41a741f0a47904a75db2981f5295732f7549c5 (diff)
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>
-rw-r--r--atuin-client/config.toml6
-rw-r--r--atuin-client/src/settings.rs2
-rw-r--r--atuin/src/command/client/search/interactive.rs74
-rw-r--r--docs/docs/key-binding.md3
4 files changed, 81 insertions, 4 deletions
diff --git a/atuin-client/config.toml b/atuin-client/config.toml
index 511662cc..29581d1f 100644
--- a/atuin-client/config.toml
+++ b/atuin-client/config.toml
@@ -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"]
diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs
index 0798a890..ea01961c 100644
--- a/atuin-client/src/settings.rs
+++ b/atuin-client/src/settings.rs
@@ -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("_")
diff --git a/atuin/src/command/client/search/interactive.rs b/atuin/src/command/client/search/interactive.rs
index 3e7e89e1..9f0e0300 100644
--- a/atuin/src/command/client/search/interactive.rs
+++ b/atuin/src/command/client/search/interactive.rs
@@ -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?;
diff --git a/docs/docs/key-binding.md b/docs/docs/key-binding.md
index c0bd2885..74dc5506 100644
--- a/docs/docs/key-binding.md
+++ b/docs/docs/key-binding.md
@@ -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 |
-