diff --git a/atuin-client/config.toml b/atuin-client/config.toml index 43b5e24c..6a8e1caa 100644 --- a/atuin-client/config.toml +++ b/atuin-client/config.toml @@ -33,3 +33,7 @@ ## which style to use ## possible values: auto, full, compact #style = "auto" + +## what to do when the escape key is pressed when searching +## possible values: return-original, return-query +# exit_mode = "return-original" diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index 4462d992..574ee3fa 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -54,6 +54,15 @@ impl FilterMode { } } +#[derive(Clone, Debug, Deserialize, Copy)] +pub enum ExitMode { + #[serde(rename = "return-original")] + ReturnOriginal, + + #[serde(rename = "return-query")] + ReturnQuery, +} + // FIXME: Can use upstream Dialect enum if https://github.com/stevedonovan/chrono-english/pull/16 is merged // FIXME: Above PR was merged, but dependency was changed to interim (fork of chrono-english) in the ... interim #[derive(Clone, Debug, Deserialize, Copy)] @@ -99,6 +108,7 @@ pub struct Settings { pub session_path: String, pub search_mode: SearchMode, pub filter_mode: FilterMode, + pub exit_mode: ExitMode, // This is automatically loaded when settings is created. Do not set in // config! Keep secrets and settings apart. pub session_token: String, @@ -278,6 +288,7 @@ impl Settings { .set_default("sync_address", "https://api.atuin.sh")? .set_default("search_mode", "fuzzy")? .set_default("filter_mode", "global")? + .set_default("exit_mode", "return-original")? .set_default("session_token", "")? .set_default("style", "auto")? .add_source( diff --git a/docs/config.md b/docs/config.md index 13aabac2..9039e094 100644 --- a/docs/config.md +++ b/docs/config.md @@ -143,6 +143,21 @@ Filter modes can still be toggled via ctrl-r search_mode = "fulltext" ``` +### `exit_mode` + +What to do when the escape key is pressed when searching + +| Value | Behaviour | +|------------------------- | --------------- | +| return-original (default) | Set the command-line to the value it had before starting search | +| return-query | Set the command-line to the search query you have entered so far | + +Pressing ctrl+c or ctrl+d will always return the original command-line value. + +``` +exit_mode = "return-query" +``` + #### `fuzzy` search syntax The "fuzzy" search syntax is based on the diff --git a/src/command/client/search/interactive.rs b/src/command/client/search/interactive.rs index 950a971d..36497db0 100644 --- a/src/command/client/search/interactive.rs +++ b/src/command/client/search/interactive.rs @@ -21,7 +21,7 @@ use atuin_client::{ database::Context, database::Database, history::History, - settings::{FilterMode, SearchMode, Settings}, + settings::{ExitMode, FilterMode, SearchMode, Settings}, }; use super::{ @@ -31,6 +31,9 @@ use super::{ }; use crate::VERSION; +const RETURN_ORIGINAL: usize = usize::MAX; +const RETURN_QUERY: usize = usize::MAX - 1; + struct State { history_count: i64, input: Cursor, @@ -59,9 +62,20 @@ impl State { Ok(results) } - fn handle_input(&mut self, input: &TermEvent, len: usize) -> Option { + fn handle_input( + &mut self, + settings: &Settings, + input: &TermEvent, + len: usize, + ) -> Option { match input { - TermEvent::Key(Key::Esc | Key::Ctrl('c' | 'd' | 'g')) => return Some(usize::MAX), + TermEvent::Key(Key::Ctrl('c' | 'd' | 'g')) => return Some(RETURN_ORIGINAL), + TermEvent::Key(Key::Esc) => { + return Some(match settings.exit_mode { + ExitMode::ReturnOriginal => RETURN_ORIGINAL, + ExitMode::ReturnQuery => RETURN_QUERY, + }) + } TermEvent::Key(Key::Char('\n')) => { return Some(self.results_state.selected()); } @@ -323,14 +337,14 @@ pub async fn history( // Handle input if let Event::Input(input) = events.next()? { - if let Some(i) = app.handle_input(&input, results.len()) { + if let Some(i) = app.handle_input(settings, &input, results.len()) { break 'render i; } } // After we receive input process the whole event channel before query/render. while let Ok(Event::Input(input)) = events.try_next() { - if let Some(i) = app.handle_input(&input, results.len()) { + if let Some(i) = app.handle_input(settings, &input, results.len()) { break 'render i; } } @@ -356,11 +370,12 @@ pub async fn history( if index < results.len() { // index is in bounds so we return that entry Ok(results.swap_remove(index).command) - } else if index == usize::MAX { - // index is max which implies an early exit + } else if index == RETURN_ORIGINAL { Ok(String::new()) } else { - // out of bounds usually implies no selected entry so we return the input + // Either: + // * index == RETURN_QUERY, in which case we should return the input + // * out of bounds -> usually implies no selected entry so we return the input Ok(app.input.into_inner()) } }