diff options
| -rw-r--r-- | atuin-client/config.toml | 5 | ||||
| -rw-r--r-- | atuin-client/src/settings.rs | 48 | ||||
| -rw-r--r-- | atuin/src/command/client/search.rs | 1 | ||||
| -rw-r--r-- | atuin/src/command/client/search/interactive.rs | 58 |
4 files changed, 109 insertions, 3 deletions
diff --git a/atuin-client/config.toml b/atuin-client/config.toml index 9e097a41..d18d9783 100644 --- a/atuin-client/config.toml +++ b/atuin-client/config.toml @@ -134,6 +134,11 @@ enter_accept = true ## the specified one. # keymap_mode = "auto" +## Cursor style in each keymap mode. If specified, the cursor style is changed +## in entering the cursor shape. Available values are "default" and +## "{blink,steady}-{block,underilne,bar}". +# keymap_cursor = { emacs = "blink-block", vim_insert = "blink-block", vim_normal = "steady-block" } + # network_connect_timeout = 5 # network_timeout = 5 diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index e57b61e3..c95c8ba5 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, convert::TryFrom, io::prelude::*, path::{Path, PathBuf}, @@ -169,6 +170,49 @@ impl KeymapMode { } } +// We want to translate the config to crossterm::cursor::SetCursorStyle, but +// the original type does not implement trait serde::Deserialize unfortunately. +// It seems impossible to implement Deserialize for external types when it is +// used in HashMap (https://stackoverflow.com/questions/67142663). We instead +// define an adapter type. +#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum)] +pub enum CursorStyle { + #[serde(rename = "default")] + DefaultUserShape, + + #[serde(rename = "blink-block")] + BlinkingBlock, + + #[serde(rename = "steady-block")] + SteadyBlock, + + #[serde(rename = "blink-underline")] + BlinkingUnderScore, + + #[serde(rename = "steady-underline")] + SteadyUnderScore, + + #[serde(rename = "blink-bar")] + BlinkingBar, + + #[serde(rename = "steady-bar")] + SteadyBar, +} + +impl CursorStyle { + pub fn as_str(&self) -> &'static str { + match self { + CursorStyle::DefaultUserShape => "DEFAULT", + CursorStyle::BlinkingBlock => "BLINKBLOCK", + CursorStyle::SteadyBlock => "STEADYBLOCK", + CursorStyle::BlinkingUnderScore => "BLINKUNDERLINE", + CursorStyle::SteadyUnderScore => "STEADYUNDERLINE", + CursorStyle::BlinkingBar => "BLINKBAR", + CursorStyle::SteadyBar => "STEADYBAR", + } + } +} + #[derive(Clone, Debug, Deserialize)] pub struct Stats { #[serde(default = "Stats::common_prefix_default")] @@ -228,6 +272,8 @@ pub struct Settings { pub show_help: bool, pub exit_mode: ExitMode, pub keymap_mode: KeymapMode, + pub keymap_mode_shell: KeymapMode, + pub keymap_cursor: HashMap<String, CursorStyle>, pub word_jump_mode: WordJumpMode, pub word_chars: String, pub scroll_context_lines: usize, @@ -466,6 +512,8 @@ impl Settings { .set_default("enter_accept", false)? .set_default("sync.records", false)? .set_default("keymap_mode", "emacs")? + .set_default("keymap_mode_shell", "auto")? + .set_default("keymap_cursor", HashMap::<String, String>::new())? .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 a929abd9..6a70ed62 100644 --- a/atuin/src/command/client/search.rs +++ b/atuin/src/command/client/search.rs @@ -161,6 +161,7 @@ impl Cmd { KeymapMode::Auto => self.keymap_mode, value => value, }; + settings.keymap_mode_shell = self.keymap_mode; let encryption_key: [u8; 32] = encryption::load_key(settings)?.into(); diff --git a/atuin/src/command/client/search/interactive.rs b/atuin/src/command/client/search/interactive.rs index 66cbb064..b5c63eae 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::{store::HistoryStore, History, HistoryStats}, - settings::{ExitMode, FilterMode, KeymapMode, SearchMode, Settings}, + settings::{CursorStyle, ExitMode, FilterMode, KeymapMode, SearchMode, Settings}, }; use super::{ @@ -64,6 +64,7 @@ pub struct State { results_len: usize, accept: bool, keymap_mode: KeymapMode, + current_cursor: Option<CursorStyle>, tab_index: usize, search: SearchState, @@ -127,6 +128,52 @@ impl State { InputAction::Continue } + fn cast_cursor_style(style: CursorStyle) -> SetCursorStyle { + match style { + CursorStyle::DefaultUserShape => SetCursorStyle::DefaultUserShape, + CursorStyle::BlinkingBlock => SetCursorStyle::BlinkingBlock, + CursorStyle::SteadyBlock => SetCursorStyle::SteadyBlock, + CursorStyle::BlinkingUnderScore => SetCursorStyle::BlinkingUnderScore, + CursorStyle::SteadyUnderScore => SetCursorStyle::SteadyUnderScore, + CursorStyle::BlinkingBar => SetCursorStyle::BlinkingBar, + CursorStyle::SteadyBar => SetCursorStyle::SteadyBar, + } + } + + fn set_keymap_cursor(&mut self, settings: &Settings, keymap_name: &str) { + let cursor_style = if keymap_name == "__clear__" { + None + } else { + settings.keymap_cursor.get(keymap_name).copied() + } + .or_else(|| self.current_cursor.map(|_| CursorStyle::DefaultUserShape)); + + if cursor_style != self.current_cursor { + if let Some(style) = cursor_style { + self.current_cursor = cursor_style; + let _ = execute!(stdout(), Self::cast_cursor_style(style)); + } + } + } + + pub fn initialize_keymap_cursor(&mut self, settings: &Settings) { + match self.keymap_mode { + KeymapMode::Emacs => self.set_keymap_cursor(settings, "emacs"), + KeymapMode::VimNormal => self.set_keymap_cursor(settings, "vim_normal"), + KeymapMode::VimInsert => self.set_keymap_cursor(settings, "vim_insert"), + KeymapMode::Auto => {} + } + } + + pub fn finalize_keymap_cursor(&mut self, settings: &Settings) { + match settings.keymap_mode_shell { + KeymapMode::Emacs => self.set_keymap_cursor(settings, "emacs"), + KeymapMode::VimNormal => self.set_keymap_cursor(settings, "vim_normal"), + KeymapMode::VimInsert => self.set_keymap_cursor(settings, "vim_insert"), + KeymapMode::Auto => self.set_keymap_cursor(settings, "__clear__"), + } + } + #[allow(clippy::too_many_lines)] #[allow(clippy::cognitive_complexity)] fn handle_key_input(&mut self, settings: &Settings, input: &KeyEvent) -> InputAction { @@ -154,7 +201,7 @@ impl State { match input.code { KeyCode::Char('c' | 'g') if ctrl => return InputAction::ReturnOriginal, KeyCode::Esc if self.keymap_mode == KeymapMode::VimInsert => { - let _ = execute!(stdout(), SetCursorStyle::SteadyBlock); + self.set_keymap_cursor(settings, "vim_normal"); self.keymap_mode = KeymapMode::VimNormal; return InputAction::Continue; } @@ -352,7 +399,7 @@ impl State { return InputAction::Redraw; } KeyCode::Char('i') if self.keymap_mode == KeymapMode::VimNormal => { - let _ = execute!(stdout(), SetCursorStyle::BlinkingBlock); + self.set_keymap_cursor(settings, "vim_insert"); self.keymap_mode = KeymapMode::VimInsert; } KeyCode::Char(c) if self.keymap_mode != KeymapMode::VimNormal => { @@ -830,8 +877,11 @@ pub async fn history( KeymapMode::Auto => KeymapMode::Emacs, value => value, }, + current_cursor: None, }; + app.initialize_keymap_cursor(settings); + let mut results = app.query_results(&mut db).await?; let mut stats: Option<HistoryStats> = None; @@ -904,6 +954,8 @@ pub async fn history( }; }; + app.finalize_keymap_cursor(settings); + if settings.inline_height > 0 { terminal.clear()?; } |
