diff options
| author | Ethan <36282608+etbyrd@users.noreply.github.com> | 2026-02-04 21:30:41 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-04 21:30:41 -0800 |
| commit | 32d2efdb5fcd2d9639bbf4966358c9392fbba2bf (patch) | |
| tree | 715cc3f7b7f525fcc382001cb1e07708ac61288f | |
| parent | feat: replace several files with a sqlite db (#3128) (diff) | |
| download | atuin-32d2efdb5fcd2d9639bbf4966358c9392fbba2bf.zip | |
feat(dotfiles): add sort and filter options to alias/var list (#3131)
groundwork for #2155. These functions can be reused when building the
interactive TUI.
Adds `--sort-by`, `--reverse`, `--name`, and `--value` flags to `atuin
dotfiles alias list` and `atuin dotfiles var list`. Also adds
`--exports-only` and `--shell-only` for vars.
happy to expand (or reduce?) on this if needed.
<details>
<summary>**Demo/Examples:**</summary>
`$atuin dotfiles alias list --reverse`
```bash
vim=nvim
py=python3
ll=ls -la
k=kubectl
gs=git status
gp=git push
gd=git diff
gc=git commit
dc=docker-compose
d=docker
```
`$atuin dotfiles alias list --sort-by value`
```bash
d=docker
dc=docker-compose
gc=git commit
gd=git diff
gp=git push
gs=git status
k=kubectl
ll=ls -la
vim=nvim
py=python3
```
`$atuin dotfiles alias list -n g`
```bash
gc=git commit
gd=git diff
gp=git push
gs=git status
```
`$atuin dotfiles var list --exports-only`
```bash
export EDITOR=vim
export LANG=en_US.UTF-8
export PAGER=less
export RUST_BACKTRACE=1
```
</details>
## Checks
- [x] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [x] I have checked that there are no existing pull requests for the
same thing
| -rw-r--r-- | crates/atuin/src/command/client/dotfiles/alias.rs | 81 | ||||
| -rw-r--r-- | crates/atuin/src/command/client/dotfiles/var.rs | 114 |
2 files changed, 180 insertions, 15 deletions
diff --git a/crates/atuin/src/command/client/dotfiles/alias.rs b/crates/atuin/src/command/client/dotfiles/alias.rs index 2a916b06..983c67f1 100644 --- a/crates/atuin/src/command/client/dotfiles/alias.rs +++ b/crates/atuin/src/command/client/dotfiles/alias.rs @@ -1,10 +1,19 @@ -use clap::Subcommand; +use clap::{Subcommand, ValueEnum}; use eyre::{Context, Result, eyre}; use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings}; use atuin_dotfiles::{shell::Alias, store::AliasStore}; +#[derive(Clone, Copy, Debug, Default, ValueEnum)] +pub enum SortBy { + /// Sort by alias name + #[default] + Name, + /// Sort by alias value + Value, +} + #[derive(Subcommand, Debug)] #[command(infer_subcommands = true)] pub enum Cmd { @@ -15,7 +24,23 @@ pub enum Cmd { Delete { name: String }, /// List all aliases - List, + List { + /// Sort results by field + #[arg(long, value_enum, default_value_t = SortBy::Name)] + sort_by: SortBy, + + /// Sort in reverse (descending) order + #[arg(long, short)] + reverse: bool, + + /// Filter aliases by name (substring match) + #[arg(long, short)] + name: Option<String>, + + /// Filter aliases by value (substring match) + #[arg(long, short)] + value: Option<String>, + }, /// Delete all aliases Clear, @@ -47,8 +72,40 @@ impl Cmd { Ok(()) } - async fn list(&self, store: &AliasStore) -> Result<()> { - let aliases = store.aliases().await?; + async fn list( + &self, + store: &AliasStore, + sort_by: SortBy, + reverse: bool, + name_filter: Option<String>, + value_filter: Option<String>, + ) -> Result<()> { + let mut aliases = store.aliases().await?; + + // Apply filters + if let Some(ref name_pattern) = name_filter { + let pattern = name_pattern.to_lowercase(); + aliases.retain(|a| a.name.to_lowercase().contains(&pattern)); + } + if let Some(ref value_pattern) = value_filter { + let pattern = value_pattern.to_lowercase(); + aliases.retain(|a| a.value.to_lowercase().contains(&pattern)); + } + + // Apply sorting + match sort_by { + SortBy::Name => { + aliases.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); + } + SortBy::Value => { + aliases.sort_by(|a, b| a.value.to_lowercase().cmp(&b.value.to_lowercase())); + } + } + + // Apply reverse if requested + if reverse { + aliases.reverse(); + } for i in aliases { println!("{}={}", i.name, i.value); @@ -109,7 +166,21 @@ impl Cmd { match self { Self::Set { name, value } => self.set(&alias_store, name.clone(), value.clone()).await, Self::Delete { name } => self.delete(&alias_store, name.clone()).await, - Self::List => self.list(&alias_store).await, + Self::List { + sort_by, + reverse, + name, + value, + } => { + self.list( + &alias_store, + *sort_by, + *reverse, + name.clone(), + value.clone(), + ) + .await + } Self::Clear => self.clear(&alias_store).await, } } diff --git a/crates/atuin/src/command/client/dotfiles/var.rs b/crates/atuin/src/command/client/dotfiles/var.rs index b48ceaa4..a63231ec 100644 --- a/crates/atuin/src/command/client/dotfiles/var.rs +++ b/crates/atuin/src/command/client/dotfiles/var.rs @@ -1,10 +1,19 @@ -use clap::Subcommand; +use clap::{Subcommand, ValueEnum}; use eyre::{Context, Result}; use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings}; use atuin_dotfiles::{shell::Var, store::var::VarStore}; +#[derive(Clone, Copy, Debug, Default, ValueEnum)] +pub enum SortBy { + /// Sort by variable name + #[default] + Name, + /// Sort by variable value + Value, +} + #[derive(Subcommand, Debug)] #[command(infer_subcommands = true)] pub enum Cmd { @@ -21,7 +30,31 @@ pub enum Cmd { Delete { name: String }, /// List all variables - List, + List { + /// Sort results by field + #[arg(long, value_enum, default_value_t = SortBy::Name)] + sort_by: SortBy, + + /// Sort in reverse (descending) order + #[arg(long, short)] + reverse: bool, + + /// Filter variables by name (substring match) + #[arg(long, short)] + name: Option<String>, + + /// Filter variables by value (substring match) + #[arg(long, short)] + value: Option<String>, + + /// Show only exported variables + #[arg(long, conflicts_with = "shell_only")] + exports_only: bool, + + /// Show only non-exported (shell) variables + #[arg(long, conflicts_with = "exports_only")] + shell_only: bool, + }, } impl Cmd { @@ -34,7 +67,7 @@ impl Cmd { println!("Setting '{show_export}{name}={value}'."); } else { println!( - "Overwriting alias '{show_export}{name}={}' with '{name}={value}'.", + "Overwriting var '{show_export}{name}={}' with '{name}={value}'.", found[0].value ); } @@ -44,15 +77,58 @@ impl Cmd { Ok(()) } - async fn list(&self, store: VarStore) -> Result<()> { - let vars = store.vars().await?; + #[allow(clippy::too_many_arguments)] + async fn list( + &self, + store: VarStore, + sort_by: SortBy, + reverse: bool, + name_filter: Option<String>, + value_filter: Option<String>, + exports_only: bool, + shell_only: bool, + ) -> Result<()> { + let mut vars = store.vars().await?; + + // Apply export/shell filters + if exports_only { + vars.retain(|v| v.export); + } + if shell_only { + vars.retain(|v| !v.export); + } - for i in vars.iter().filter(|v| !v.export) { - println!("{}={}", i.name, i.value); + // Apply name/value filters + if let Some(ref name_pattern) = name_filter { + let pattern = name_pattern.to_lowercase(); + vars.retain(|v| v.name.to_lowercase().contains(&pattern)); + } + if let Some(ref value_pattern) = value_filter { + let pattern = value_pattern.to_lowercase(); + vars.retain(|v| v.value.to_lowercase().contains(&pattern)); + } + + // Apply sorting + match sort_by { + SortBy::Name => { + vars.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); + } + SortBy::Value => { + vars.sort_by(|a, b| a.value.to_lowercase().cmp(&b.value.to_lowercase())); + } + } + + // Apply reverse if requested + if reverse { + vars.reverse(); } - for i in vars.iter().filter(|v| v.export) { - println!("export {}={}", i.name, i.value); + for i in vars { + if i.export { + println!("export {}={}", i.name, i.value); + } else { + println!("{}={}", i.name, i.value); + } } Ok(()) @@ -97,7 +173,25 @@ impl Cmd { .await } Self::Delete { name } => self.delete(var_store, name.clone()).await, - Self::List => self.list(var_store).await, + Self::List { + sort_by, + reverse, + name, + value, + exports_only, + shell_only, + } => { + self.list( + var_store, + *sort_by, + *reverse, + name.clone(), + value.clone(), + *exports_only, + *shell_only, + ) + .await + } } } } |
