From 0639ff49604a44b3c487110860f511ac9b426e60 Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Thu, 2 May 2024 16:53:38 +0100 Subject: [PATCH] fix(dotfiles): allow clearing aliases, disable import (#1995) * fix(dotfiles): allow clearing aliases, disable import At the moment there are far too many edge cases to handle importing aliases. 1. We need an interactive shell to print aliases. Without it, most shells won't report much. 2. Many people have their shells print things on startup (graphics, fortunes, etc). This could be detected as an attempt to set an alias. Rather than spend the next year finding import edge cases, I'm disabling it for now. There's probably a better way we can do this? * clippy --- Cargo.lock | 1 + Cargo.toml | 2 +- crates/atuin-client/src/sync.rs | 6 +-- crates/atuin-dotfiles/src/shell.rs | 2 +- crates/atuin/Cargo.toml | 1 + .../src/command/client/dotfiles/alias.rs | 41 ++++++++++++++----- .../src/command/client/search/interactive.rs | 8 +--- crates/atuin/src/command/client/stats.rs | 2 +- 8 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d12dedf9..9b002bd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,6 +207,7 @@ dependencies = [ "itertools", "log", "ratatui", + "regex", "rpassword", "runtime-format", "rustix", diff --git a/Cargo.toml b/Cargo.toml index 03d7bfb7..6ff40fb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ exclude = ["ui/backend"] [workspace.package] version = "18.2.0" authors = ["Ellie Huxtable "] -rust-version = "1.67" +rust-version = "1.70" license = "MIT" homepage = "https://atuin.sh" repository = "https://github.com/atuinsh/atuin" diff --git a/crates/atuin-client/src/sync.rs b/crates/atuin-client/src/sync.rs index 1f0d3dd8..0a5021cb 100644 --- a/crates/atuin-client/src/sync.rs +++ b/crates/atuin-client/src/sync.rs @@ -37,7 +37,7 @@ async fn sync_download( key: &Key, force: bool, client: &api_client::Client<'_>, - db: &(impl Database + Send), + db: &impl Database, ) -> Result<(i64, i64)> { debug!("starting sync download"); @@ -127,7 +127,7 @@ async fn sync_upload( key: &Key, _force: bool, client: &api_client::Client<'_>, - db: &(impl Database + Send), + db: &impl Database, ) -> Result<()> { debug!("starting sync upload"); @@ -188,7 +188,7 @@ async fn sync_upload( Ok(()) } -pub async fn sync(settings: &Settings, force: bool, db: &(impl Database + Send)) -> Result<()> { +pub async fn sync(settings: &Settings, force: bool, db: &impl Database) -> Result<()> { let client = api_client::Client::new( &settings.sync_address, &settings.session_token, diff --git a/crates/atuin-dotfiles/src/shell.rs b/crates/atuin-dotfiles/src/shell.rs index d4cacf8f..3e139819 100644 --- a/crates/atuin-dotfiles/src/shell.rs +++ b/crates/atuin-dotfiles/src/shell.rs @@ -136,7 +136,7 @@ pub fn existing_aliases(shell: Option) -> Result, ShellError> /// Import aliases from the current shell /// This will not import aliases already in the store /// Returns aliases that were set -pub async fn import_aliases(store: AliasStore) -> Result> { +pub async fn import_aliases(store: &AliasStore) -> Result> { let shell_aliases = existing_aliases(None)?; let store_aliases = store.aliases().await?; diff --git a/crates/atuin/Cargo.toml b/crates/atuin/Cargo.toml index 2915317a..91c4ed4d 100644 --- a/crates/atuin/Cargo.toml +++ b/crates/atuin/Cargo.toml @@ -82,6 +82,7 @@ uuid = { workspace = true } unicode-segmentation = "1.11.0" serde_yaml = "0.9.32" sysinfo = "0.30.7" +regex="1.10.4" [target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies] cli-clipboard = { version = "0.4.0", optional = true } diff --git a/crates/atuin/src/command/client/dotfiles/alias.rs b/crates/atuin/src/command/client/dotfiles/alias.rs index 6456a8b0..f7d402b6 100644 --- a/crates/atuin/src/command/client/dotfiles/alias.rs +++ b/crates/atuin/src/command/client/dotfiles/alias.rs @@ -1,5 +1,5 @@ use clap::Subcommand; -use eyre::{Context, Result}; +use eyre::{eyre, Context, Result}; use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings}; @@ -17,12 +17,19 @@ pub enum Cmd { /// List all aliases List, - /// Import aliases set in the current shell - Import, + /// Delete all aliases + Clear, + // There are too many edge cases to parse at the moment. Disable for now. + // Import, } impl Cmd { - async fn set(&self, store: AliasStore, name: String, value: String) -> Result<()> { + async fn set(&self, store: &AliasStore, name: String, value: String) -> Result<()> { + let illegal_char = regex::Regex::new("[ \t\n&();<>|\\\"'`$/]").unwrap(); + if illegal_char.is_match(name.as_str()) { + return Err(eyre!("Illegal character in alias name")); + } + let aliases = store.aliases().await?; let found: Vec = aliases.into_iter().filter(|a| a.name == name).collect(); @@ -40,7 +47,7 @@ impl Cmd { Ok(()) } - async fn list(&self, store: AliasStore) -> Result<()> { + async fn list(&self, store: &AliasStore) -> Result<()> { let aliases = store.aliases().await?; for i in aliases { @@ -50,7 +57,17 @@ impl Cmd { Ok(()) } - async fn delete(&self, store: AliasStore, name: String) -> Result<()> { + async fn clear(&self, store: &AliasStore) -> Result<()> { + let aliases = store.aliases().await?; + + for i in aliases { + self.delete(store, i.name).await?; + } + + Ok(()) + } + + async fn delete(&self, store: &AliasStore, name: String) -> Result<()> { let mut aliases = store.aliases().await?.into_iter(); if let Some(alias) = aliases.find(|alias| alias.name == name) { println!("Deleting '{name}={}'.", alias.value); @@ -61,7 +78,8 @@ impl Cmd { Ok(()) } - async fn import(&self, store: AliasStore) -> Result<()> { + /* + async fn import(&self, store: &AliasStore) -> Result<()> { let aliases = atuin_dotfiles::shell::import_aliases(store).await?; for i in aliases { @@ -70,6 +88,7 @@ impl Cmd { Ok(()) } + */ pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> { if !settings.dotfiles.enabled { @@ -86,10 +105,10 @@ impl Cmd { let alias_store = AliasStore::new(store, host_id, encryption_key); 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::Import => self.import(alias_store).await, + 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::Clear => self.clear(&alias_store).await, } } } diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index d24fee0d..9b41950e 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -308,13 +308,7 @@ impl State { // handle keymap specific keybindings. match self.keymap_mode { KeymapMode::VimNormal => match input.code { - KeyCode::Char('/') if !ctrl => { - self.search.input.clear(); - self.set_keymap_cursor(settings, "vim_insert"); - self.keymap_mode = KeymapMode::VimInsert; - return InputAction::Continue; - } - KeyCode::Char('?') if !ctrl => { + KeyCode::Char('?' | '/') if !ctrl => { self.search.input.clear(); self.set_keymap_cursor(settings, "vim_insert"); self.keymap_mode = KeymapMode::VimInsert; diff --git a/crates/atuin/src/command/client/stats.rs b/crates/atuin/src/command/client/stats.rs index fd480438..2de70d1d 100644 --- a/crates/atuin/src/command/client/stats.rs +++ b/crates/atuin/src/command/client/stats.rs @@ -13,7 +13,7 @@ use atuin_history::stats::{compute, pretty_print}; #[derive(Parser, Debug)] #[command(infer_subcommands = true)] pub struct Cmd { - /// Compute statistics for the specified period, leave blank for statistics since the beginning. See https://docs.atuin.sh/reference/stats/ for more details. + /// Compute statistics for the specified period, leave blank for statistics since the beginning. See [this](https://docs.atuin.sh/reference/stats/) for more details. period: Vec, /// How many top commands to list