aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEthan <36282608+etbyrd@users.noreply.github.com>2026-02-04 21:30:41 -0800
committerGitHub <noreply@github.com>2026-02-04 21:30:41 -0800
commit32d2efdb5fcd2d9639bbf4966358c9392fbba2bf (patch)
tree715cc3f7b7f525fcc382001cb1e07708ac61288f
parentfeat: replace several files with a sqlite db (#3128) (diff)
downloadatuin-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.rs81
-rw-r--r--crates/atuin/src/command/client/dotfiles/var.rs114
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
+ }
}
}
}