From da8cf558e174ff08f44cc9c1105fc0e345e6ea9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janne=20He=C3=9F?= Date: Tue, 13 Feb 2024 20:41:25 +0100 Subject: [PATCH] feat(client): Allow sorting filter modes Closes #1417 Closes #1654 --- atuin-client/config.toml | 7 +- atuin-client/src/settings.rs | 9 ++- atuin/src/command/client/history.rs | 7 +- atuin/src/command/client/search.rs | 18 +++-- atuin/src/command/client/search/engines.rs | 11 ++- atuin/src/command/client/search/engines/db.rs | 2 +- .../src/command/client/search/engines/skim.rs | 13 ++-- .../src/command/client/search/interactive.rs | 78 +++++++++++-------- 8 files changed, 95 insertions(+), 50 deletions(-) diff --git a/atuin-client/config.toml b/atuin-client/config.toml index facc0bd4c98..47cfcef457a 100644 --- a/atuin-client/config.toml +++ b/atuin-client/config.toml @@ -40,10 +40,15 @@ ## possible values: prefix, fulltext, fuzzy, skim # search_mode = "fuzzy" -## which filter mode to use +## which filter mode to use by default ## possible values: global, host, session, directory +## defaults to the first element of 'filter_modes' # filter_mode = "global" +## which filter modes are available in which order. +## 'workspace' is skipped when not in a workspace or disabled +# filter_modes = [ "workspace", "global", "host"; "session", "directory" ] + ## With workspace filtering enabled, Atuin will filter for commands executed ## in any directory within a git repository tree (default: false) # workspaces = false diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index f4c47c64580..0e99e85ad41 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -313,7 +313,8 @@ pub struct Settings { pub key_path: String, pub session_path: String, pub search_mode: SearchMode, - pub filter_mode: FilterMode, + pub filter_mode: Option, + pub filter_modes: Vec, pub filter_mode_shell_up_key_binding: Option, pub search_mode_shell_up_key_binding: Option, pub shell_up_key_binding: bool, @@ -552,7 +553,11 @@ impl Settings { .set_default("sync_address", "https://api.atuin.sh")? .set_default("sync_frequency", "10m")? .set_default("search_mode", "fuzzy")? - .set_default("filter_mode", "global")? + .set_default("filter_mode", None::)? + .set_default( + "filter_modes", + vec!["workspace", "global", "host", "session"], + )? .set_default("style", "auto")? .set_default("inline_height", 0)? .set_default("show_preview", false)? diff --git a/atuin/src/command/client/history.rs b/atuin/src/command/client/history.rs index b0a70b810d7..ba3829a6ed6 100644 --- a/atuin/src/command/client/history.rs +++ b/atuin/src/command/client/history.rs @@ -391,7 +391,12 @@ impl Cmd { (true, true) => [Session, Directory], (true, false) => [Session, Global], (false, true) => [Global, Directory], - (false, false) => [settings.filter_mode, Global], + (false, false) => [ + settings + .filter_mode + .unwrap_or(*settings.filter_modes.first().unwrap_or(&Global)), + Global, + ], }; let history = db diff --git a/atuin/src/command/client/search.rs b/atuin/src/command/client/search.rs index 4a70cb9874d..ebc0d447355 100644 --- a/atuin/src/command/client/search.rs +++ b/atuin/src/command/client/search.rs @@ -155,7 +155,7 @@ impl Cmd { settings.search_mode = self.search_mode.unwrap(); } if self.filter_mode.is_some() { - settings.filter_mode = self.filter_mode.unwrap(); + settings.filter_mode = self.filter_mode; } if self.inline_height.is_some() { settings.inline_height = self.inline_height.unwrap(); @@ -267,11 +267,17 @@ async fn run_non_interactive( }; let dir = dir.unwrap_or_else(|| "/".to_string()); - let filter_mode = if settings.workspaces && utils::has_git_dir(dir.as_str()) { - FilterMode::Workspace - } else { - settings.filter_mode - }; + let filter_mode = settings.filter_mode.unwrap_or( + *settings + .filter_modes + .iter() + .find(|item| { + *item != &FilterMode::Workspace + || !settings.workspaces + || utils::has_git_dir(dir.as_str()) + }) + .unwrap_or(&FilterMode::Global), + ); let results = db .search( diff --git a/atuin/src/command/client/search/engines.rs b/atuin/src/command/client/search/engines.rs index 105ce147e7b..b80ef7047f0 100644 --- a/atuin/src/command/client/search/engines.rs +++ b/atuin/src/command/client/search/engines.rs @@ -20,7 +20,8 @@ pub fn engine(search_mode: SearchMode) -> Box { pub struct SearchState { pub input: Cursor, - pub filter_mode: FilterMode, + pub filter_mode_index: usize, + pub available_filter_modes: Vec, pub context: Context, } @@ -35,7 +36,13 @@ pub trait SearchEngine: Send + Sync + 'static { async fn query(&mut self, state: &SearchState, db: &mut dyn Database) -> Result> { if state.input.as_str().is_empty() { Ok(db - .list(&[state.filter_mode], &state.context, Some(200), true, false) + .list( + &[state.available_filter_modes[state.filter_mode_index]], + &state.context, + Some(200), + true, + false, + ) .await? .into_iter() .collect::>()) diff --git a/atuin/src/command/client/search/engines/db.rs b/atuin/src/command/client/search/engines/db.rs index b4f24561de2..4d1666c9930 100644 --- a/atuin/src/command/client/search/engines/db.rs +++ b/atuin/src/command/client/search/engines/db.rs @@ -18,7 +18,7 @@ impl SearchEngine for Search { Ok(db .search( self.0, - state.filter_mode, + state.available_filter_modes[state.filter_mode_index], &state.context, state.input.as_str(), OptFilters { diff --git a/atuin/src/command/client/search/engines/skim.rs b/atuin/src/command/client/search/engines/skim.rs index e24eca7b045..ea5948a9120 100644 --- a/atuin/src/command/client/search/engines/skim.rs +++ b/atuin/src/command/client/search/engines/skim.rs @@ -59,25 +59,26 @@ async fn fuzzy_search( .as_ref() .and_then(|git_root| git_root.to_str()) .unwrap_or(&context.cwd); - match state.filter_mode { - FilterMode::Global => {} + match state.available_filter_modes.get(state.filter_mode_index) { + Some(FilterMode::Global) => {} // we aggregate host by ',' separating them - FilterMode::Host + Some(FilterMode::Host) if history .hostname .split(',') .contains(&context.hostname.as_str()) => {} // we aggregate session by concattenating them. // sessions are 32 byte simple uuid formats - FilterMode::Session + Some(FilterMode::Session) if history .session .as_bytes() .chunks(32) .contains(&context.session.as_bytes()) => {} // we aggregate directory by ':' separating them - FilterMode::Directory if history.cwd.split(':').contains(&context.cwd.as_str()) => {} - FilterMode::Workspace if history.cwd.split(':').contains(&git_root) => {} + Some(FilterMode::Directory) + if history.cwd.split(':').contains(&context.cwd.as_str()) => {} + Some(FilterMode::Workspace) if history.cwd.split(':').contains(&git_root) => {} _ => continue, } #[allow(clippy::cast_lossless, clippy::cast_precision_loss)] diff --git a/atuin/src/command/client/search/interactive.rs b/atuin/src/command/client/search/interactive.rs index 7680ce5321f..3f74e379274 100644 --- a/atuin/src/command/client/search/interactive.rs +++ b/atuin/src/command/client/search/interactive.rs @@ -381,27 +381,8 @@ impl State { } KeyCode::Char('u') if ctrl => self.search.input.clear(), KeyCode::Char('r') if ctrl => { - let filter_modes = if settings.workspaces && self.search.context.git_root.is_some() - { - vec![ - FilterMode::Global, - FilterMode::Host, - FilterMode::Session, - FilterMode::Directory, - FilterMode::Workspace, - ] - } else { - vec![ - FilterMode::Global, - FilterMode::Host, - FilterMode::Session, - FilterMode::Directory, - ] - }; - - let i = self.search.filter_mode as usize; - let i = (i + 1) % filter_modes.len(); - self.search.filter_mode = filter_modes[i]; + self.search.filter_mode_index = + (self.search.filter_mode_index + 1) % self.search.available_filter_modes.len(); } KeyCode::Char('s') if ctrl => { self.switched_search_mode = true; @@ -712,7 +693,10 @@ impl State { let (pref, mode) = if self.switched_search_mode { (" SRCH:", self.search_mode.as_str()) } else { - ("", self.search.filter_mode.as_str()) + ( + "", + self.search.available_filter_modes[self.search.filter_mode_index].as_str(), + ) }; let mode_width = MAX_WIDTH - pref.len(); // sanity check to ensure we don't exceed the layout limits @@ -882,6 +866,28 @@ pub async fn history( } else { settings.search_mode }; + let mut available_filter_modes = settings + .filter_modes + .clone() + .into_iter() + .filter(|item| { + *item != FilterMode::Workspace || !settings.workspaces || context.git_root.is_some() + }) + .collect::>(); + if available_filter_modes.is_empty() { + available_filter_modes = vec![ + FilterMode::Workspace, + FilterMode::Global, + FilterMode::Host, + FilterMode::Session, + ] + .into_iter() + .filter(|item| { + *item != FilterMode::Workspace || !settings.workspaces || context.git_root.is_some() + }) + .collect::>(); + } + let mut app = State { history_count, results_state: ListState::default(), @@ -891,15 +897,24 @@ pub async fn history( tab_index: 0, search: SearchState { input, - filter_mode: if settings.workspaces && context.git_root.is_some() { - FilterMode::Workspace - } else if settings.shell_up_key_binding { - settings - .filter_mode_shell_up_key_binding - .unwrap_or(settings.filter_mode) + filter_mode_index: if settings.shell_up_key_binding { + available_filter_modes + .iter() + .position(|&item| { + item == settings + .filter_mode_shell_up_key_binding + .unwrap_or(FilterMode::Global) + }) + .unwrap_or_default() + } else if let Some(filter_mode) = settings.filter_mode { + available_filter_modes + .iter() + .position(|&item| item == filter_mode) + .unwrap_or_default() } else { - settings.filter_mode + 0 }, + available_filter_modes, context, }, engine: engines::engine(search_mode), @@ -928,7 +943,7 @@ pub async fn history( terminal.draw(|f| app.draw(f, &results, stats.clone(), settings))?; let initial_input = app.search.input.as_str().to_owned(); - let initial_filter_mode = app.search.filter_mode; + let initial_filter_mode = app.search.available_filter_modes[app.search.filter_mode_index]; let initial_search_mode = app.search_mode; let event_ready = tokio::task::spawn_blocking(|| event::poll(Duration::from_millis(250))); @@ -978,7 +993,8 @@ pub async fn history( } if initial_input != app.search.input.as_str() - || initial_filter_mode != app.search.filter_mode + || initial_filter_mode + != app.search.available_filter_modes[app.search.filter_mode_index] || initial_search_mode != app.search_mode { results = app.query_results(&mut db).await?;