aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--atuin-client/config.toml5
-rw-r--r--atuin-client/src/settings.rs48
-rw-r--r--atuin/src/command/client/search.rs1
-rw-r--r--atuin/src/command/client/search/interactive.rs58
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()?;
}