Skip to content

Commit da8cf55

Browse files
committed
feat(client): Allow sorting filter modes
Closes #1417 Closes #1654
1 parent 87e19df commit da8cf55

File tree

8 files changed

+95
-50
lines changed

8 files changed

+95
-50
lines changed

atuin-client/config.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,15 @@
4040
## possible values: prefix, fulltext, fuzzy, skim
4141
# search_mode = "fuzzy"
4242

43-
## which filter mode to use
43+
## which filter mode to use by default
4444
## possible values: global, host, session, directory
45+
## defaults to the first element of 'filter_modes'
4546
# filter_mode = "global"
4647

48+
## which filter modes are available in which order.
49+
## 'workspace' is skipped when not in a workspace or disabled
50+
# filter_modes = [ "workspace", "global", "host"; "session", "directory" ]
51+
4752
## With workspace filtering enabled, Atuin will filter for commands executed
4853
## in any directory within a git repository tree (default: false)
4954
# workspaces = false

atuin-client/src/settings.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ pub struct Settings {
313313
pub key_path: String,
314314
pub session_path: String,
315315
pub search_mode: SearchMode,
316-
pub filter_mode: FilterMode,
316+
pub filter_mode: Option<FilterMode>,
317+
pub filter_modes: Vec<FilterMode>,
317318
pub filter_mode_shell_up_key_binding: Option<FilterMode>,
318319
pub search_mode_shell_up_key_binding: Option<SearchMode>,
319320
pub shell_up_key_binding: bool,
@@ -552,7 +553,11 @@ impl Settings {
552553
.set_default("sync_address", "https://api.atuin.sh")?
553554
.set_default("sync_frequency", "10m")?
554555
.set_default("search_mode", "fuzzy")?
555-
.set_default("filter_mode", "global")?
556+
.set_default("filter_mode", None::<String>)?
557+
.set_default(
558+
"filter_modes",
559+
vec!["workspace", "global", "host", "session"],
560+
)?
556561
.set_default("style", "auto")?
557562
.set_default("inline_height", 0)?
558563
.set_default("show_preview", false)?

atuin/src/command/client/history.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,12 @@ impl Cmd {
391391
(true, true) => [Session, Directory],
392392
(true, false) => [Session, Global],
393393
(false, true) => [Global, Directory],
394-
(false, false) => [settings.filter_mode, Global],
394+
(false, false) => [
395+
settings
396+
.filter_mode
397+
.unwrap_or(*settings.filter_modes.first().unwrap_or(&Global)),
398+
Global,
399+
],
395400
};
396401

397402
let history = db

atuin/src/command/client/search.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ impl Cmd {
155155
settings.search_mode = self.search_mode.unwrap();
156156
}
157157
if self.filter_mode.is_some() {
158-
settings.filter_mode = self.filter_mode.unwrap();
158+
settings.filter_mode = self.filter_mode;
159159
}
160160
if self.inline_height.is_some() {
161161
settings.inline_height = self.inline_height.unwrap();
@@ -267,11 +267,17 @@ async fn run_non_interactive(
267267
};
268268

269269
let dir = dir.unwrap_or_else(|| "/".to_string());
270-
let filter_mode = if settings.workspaces && utils::has_git_dir(dir.as_str()) {
271-
FilterMode::Workspace
272-
} else {
273-
settings.filter_mode
274-
};
270+
let filter_mode = settings.filter_mode.unwrap_or(
271+
*settings
272+
.filter_modes
273+
.iter()
274+
.find(|item| {
275+
*item != &FilterMode::Workspace
276+
|| !settings.workspaces
277+
|| utils::has_git_dir(dir.as_str())
278+
})
279+
.unwrap_or(&FilterMode::Global),
280+
);
275281

276282
let results = db
277283
.search(

atuin/src/command/client/search/engines.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ pub fn engine(search_mode: SearchMode) -> Box<dyn SearchEngine> {
2020

2121
pub struct SearchState {
2222
pub input: Cursor,
23-
pub filter_mode: FilterMode,
23+
pub filter_mode_index: usize,
24+
pub available_filter_modes: Vec<FilterMode>,
2425
pub context: Context,
2526
}
2627

@@ -35,7 +36,13 @@ pub trait SearchEngine: Send + Sync + 'static {
3536
async fn query(&mut self, state: &SearchState, db: &mut dyn Database) -> Result<Vec<History>> {
3637
if state.input.as_str().is_empty() {
3738
Ok(db
38-
.list(&[state.filter_mode], &state.context, Some(200), true, false)
39+
.list(
40+
&[state.available_filter_modes[state.filter_mode_index]],
41+
&state.context,
42+
Some(200),
43+
true,
44+
false,
45+
)
3946
.await?
4047
.into_iter()
4148
.collect::<Vec<_>>())

atuin/src/command/client/search/engines/db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl SearchEngine for Search {
1818
Ok(db
1919
.search(
2020
self.0,
21-
state.filter_mode,
21+
state.available_filter_modes[state.filter_mode_index],
2222
&state.context,
2323
state.input.as_str(),
2424
OptFilters {

atuin/src/command/client/search/engines/skim.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,26 @@ async fn fuzzy_search(
5959
.as_ref()
6060
.and_then(|git_root| git_root.to_str())
6161
.unwrap_or(&context.cwd);
62-
match state.filter_mode {
63-
FilterMode::Global => {}
62+
match state.available_filter_modes.get(state.filter_mode_index) {
63+
Some(FilterMode::Global) => {}
6464
// we aggregate host by ',' separating them
65-
FilterMode::Host
65+
Some(FilterMode::Host)
6666
if history
6767
.hostname
6868
.split(',')
6969
.contains(&context.hostname.as_str()) => {}
7070
// we aggregate session by concattenating them.
7171
// sessions are 32 byte simple uuid formats
72-
FilterMode::Session
72+
Some(FilterMode::Session)
7373
if history
7474
.session
7575
.as_bytes()
7676
.chunks(32)
7777
.contains(&context.session.as_bytes()) => {}
7878
// we aggregate directory by ':' separating them
79-
FilterMode::Directory if history.cwd.split(':').contains(&context.cwd.as_str()) => {}
80-
FilterMode::Workspace if history.cwd.split(':').contains(&git_root) => {}
79+
Some(FilterMode::Directory)
80+
if history.cwd.split(':').contains(&context.cwd.as_str()) => {}
81+
Some(FilterMode::Workspace) if history.cwd.split(':').contains(&git_root) => {}
8182
_ => continue,
8283
}
8384
#[allow(clippy::cast_lossless, clippy::cast_precision_loss)]

atuin/src/command/client/search/interactive.rs

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -381,27 +381,8 @@ impl State {
381381
}
382382
KeyCode::Char('u') if ctrl => self.search.input.clear(),
383383
KeyCode::Char('r') if ctrl => {
384-
let filter_modes = if settings.workspaces && self.search.context.git_root.is_some()
385-
{
386-
vec![
387-
FilterMode::Global,
388-
FilterMode::Host,
389-
FilterMode::Session,
390-
FilterMode::Directory,
391-
FilterMode::Workspace,
392-
]
393-
} else {
394-
vec![
395-
FilterMode::Global,
396-
FilterMode::Host,
397-
FilterMode::Session,
398-
FilterMode::Directory,
399-
]
400-
};
401-
402-
let i = self.search.filter_mode as usize;
403-
let i = (i + 1) % filter_modes.len();
404-
self.search.filter_mode = filter_modes[i];
384+
self.search.filter_mode_index =
385+
(self.search.filter_mode_index + 1) % self.search.available_filter_modes.len();
405386
}
406387
KeyCode::Char('s') if ctrl => {
407388
self.switched_search_mode = true;
@@ -712,7 +693,10 @@ impl State {
712693
let (pref, mode) = if self.switched_search_mode {
713694
(" SRCH:", self.search_mode.as_str())
714695
} else {
715-
("", self.search.filter_mode.as_str())
696+
(
697+
"",
698+
self.search.available_filter_modes[self.search.filter_mode_index].as_str(),
699+
)
716700
};
717701
let mode_width = MAX_WIDTH - pref.len();
718702
// sanity check to ensure we don't exceed the layout limits
@@ -882,6 +866,28 @@ pub async fn history(
882866
} else {
883867
settings.search_mode
884868
};
869+
let mut available_filter_modes = settings
870+
.filter_modes
871+
.clone()
872+
.into_iter()
873+
.filter(|item| {
874+
*item != FilterMode::Workspace || !settings.workspaces || context.git_root.is_some()
875+
})
876+
.collect::<Vec<_>>();
877+
if available_filter_modes.is_empty() {
878+
available_filter_modes = vec![
879+
FilterMode::Workspace,
880+
FilterMode::Global,
881+
FilterMode::Host,
882+
FilterMode::Session,
883+
]
884+
.into_iter()
885+
.filter(|item| {
886+
*item != FilterMode::Workspace || !settings.workspaces || context.git_root.is_some()
887+
})
888+
.collect::<Vec<_>>();
889+
}
890+
885891
let mut app = State {
886892
history_count,
887893
results_state: ListState::default(),
@@ -891,15 +897,24 @@ pub async fn history(
891897
tab_index: 0,
892898
search: SearchState {
893899
input,
894-
filter_mode: if settings.workspaces && context.git_root.is_some() {
895-
FilterMode::Workspace
896-
} else if settings.shell_up_key_binding {
897-
settings
898-
.filter_mode_shell_up_key_binding
899-
.unwrap_or(settings.filter_mode)
900+
filter_mode_index: if settings.shell_up_key_binding {
901+
available_filter_modes
902+
.iter()
903+
.position(|&item| {
904+
item == settings
905+
.filter_mode_shell_up_key_binding
906+
.unwrap_or(FilterMode::Global)
907+
})
908+
.unwrap_or_default()
909+
} else if let Some(filter_mode) = settings.filter_mode {
910+
available_filter_modes
911+
.iter()
912+
.position(|&item| item == filter_mode)
913+
.unwrap_or_default()
900914
} else {
901-
settings.filter_mode
915+
0
902916
},
917+
available_filter_modes,
903918
context,
904919
},
905920
engine: engines::engine(search_mode),
@@ -928,7 +943,7 @@ pub async fn history(
928943
terminal.draw(|f| app.draw(f, &results, stats.clone(), settings))?;
929944

930945
let initial_input = app.search.input.as_str().to_owned();
931-
let initial_filter_mode = app.search.filter_mode;
946+
let initial_filter_mode = app.search.available_filter_modes[app.search.filter_mode_index];
932947
let initial_search_mode = app.search_mode;
933948

934949
let event_ready = tokio::task::spawn_blocking(|| event::poll(Duration::from_millis(250)));
@@ -978,7 +993,8 @@ pub async fn history(
978993
}
979994

980995
if initial_input != app.search.input.as_str()
981-
|| initial_filter_mode != app.search.filter_mode
996+
|| initial_filter_mode
997+
!= app.search.available_filter_modes[app.search.filter_mode_index]
982998
|| initial_search_mode != app.search_mode
983999
{
9841000
results = app.query_results(&mut db).await?;

0 commit comments

Comments
 (0)