diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-06-12 17:16:19 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-06-12 17:16:19 +0200 |
| commit | 2ca7dd57b12861e8c9bbc9238cda612e0ff22ff3 (patch) | |
| tree | 302a644f6a50d60cc8304c4498fe6bbb72ddaaa9 /crates/turtle/src/command | |
| parent | feat(server): Really make users stateless (with tests) (diff) | |
| download | atuin-2ca7dd57b12861e8c9bbc9238cda612e0ff22ff3.zip | |
chore(treewide): Cleanup themes
Diffstat (limited to '')
19 files changed, 167 insertions, 473 deletions
diff --git a/crates/turtle/src/command/client.rs b/crates/turtle/src/command/client.rs index 9ab28e15..0c97f945 100644 --- a/crates/turtle/src/command/client.rs +++ b/crates/turtle/src/command/client.rs @@ -5,7 +5,7 @@ use clap::Subcommand; use eyre::{Result, WrapErr}; use crate::atuin_client::{ - database::ClientSqlite, record::sqlite_store::SqliteStore, settings::Settings, theme, + database::ClientSqlite, record::sqlite_store::SqliteStore, settings::Settings, }; use tracing_appender::rolling::{RollingFileAppender, Rotation}; use tracing_subscriber::{ @@ -40,13 +40,8 @@ fn cleanup_old_logs(log_dir: &Path, prefix: &str, retention_days: u64) { } } -#[cfg(feature = "sync")] -mod sync; - -#[cfg(feature = "daemon")] -mod daemon; - mod config; +mod daemon; mod default_config; mod history; mod info; @@ -55,6 +50,7 @@ mod search; mod server; mod stats; mod store; +mod sync; mod wrapped; #[derive(Subcommand, Debug)] @@ -67,7 +63,6 @@ pub(crate) enum Cmd { /// Interactive history search Search(search::Cmd), - #[cfg(feature = "sync")] #[command(subcommand)] /// Request a sync or view sync status Sync(sync::Cmd), @@ -96,7 +91,6 @@ pub(crate) enum Cmd { Wrapped { year: Option<i32> }, /// *Experimental* Manage the background daemon - #[cfg(feature = "daemon")] #[command()] Daemon(daemon::Cmd), @@ -134,9 +128,8 @@ impl Cmd { // doing anything else. History commands are performance-sensitive and run before and after // every shell command, so we want to skip any unnecessary initialization for them. let settings = Settings::new().wrap_err("could not load client settings")?; - let theme_manager = theme::ThemeManager::new(settings.theme.debug, None); - runtime.block_on(self.run_inner(settings, theme_manager)) + runtime.block_on(self.run_inner(settings)) }; runtime.shutdown_timeout(std::time::Duration::from_millis(50)); @@ -145,11 +138,7 @@ impl Cmd { } #[expect(clippy::too_many_lines)] - async fn run_inner( - self, - mut settings: Settings, - mut theme_manager: theme::ThemeManager, - ) -> Result<()> { + async fn run_inner(self, mut settings: Settings) -> Result<()> { // ATUIN_LOG env var overrides config file level settings let env_log_set = std::env::var("ATUIN_LOG").is_ok(); @@ -162,19 +151,11 @@ impl Cmd { let use_search_logging = is_interactive_search && settings.logs.search_enabled(); // Use file-based logging for daemon - #[cfg(feature = "daemon")] let use_daemon_logging = matches!(&self, Self::Daemon(_)) && settings.logs.daemon_enabled(); - #[cfg(not(feature = "daemon"))] - let use_daemon_logging = false; - // Check if daemon should also log to console - #[cfg(feature = "daemon")] let daemon_show_logs = matches!(&self, Self::Daemon(cmd) if cmd.show_logs()); - #[cfg(not(feature = "daemon"))] - let daemon_show_logs = false; - // Set up span timing JSON logs if ATUIN_SPAN is set let span_path = std::env::var("ATUIN_SPAN").ok().map(|p| { if p.is_empty() { @@ -317,14 +298,11 @@ impl Cmd { let db = ClientSqlite::new(db_path, settings.local_timeout).await?; let sqlite_store = SqliteStore::new(record_store_path, settings.local_timeout).await?; - let theme_name = settings.theme.name.clone(); - let theme = theme_manager.load_theme(theme_name.as_str(), settings.theme.max_depth); - match self { - Self::Stats(stats) => stats.run(&db, &settings, theme).await, - Self::Search(search) => search.run(db, &mut settings, sqlite_store, theme).await, + Self::Stats(stats) => stats.run(&db, &settings).await, + Self::Search(search) => search.run(db, &mut settings, sqlite_store).await, + Self::Wrapped { year } => wrapped::run(year, &db, &settings).await, - #[cfg(feature = "sync")] Self::Sync(sync) => sync.run(settings, &db, sqlite_store).await, Self::Store(store) => store.run(&settings, &db, sqlite_store).await, @@ -336,9 +314,6 @@ impl Cmd { Ok(()) } - Self::Wrapped { year } => wrapped::run(year, &db, &settings, theme).await, - - #[cfg(feature = "daemon")] Self::Daemon(cmd) => cmd.run(settings, sqlite_store, db).await, Self::History(_) | Self::Init(_) | Self::Config(_) | Self::Server(_) => { diff --git a/crates/turtle/src/command/client/daemon.rs b/crates/turtle/src/command/client/daemon.rs index cb5dd118..41cb04fe 100644 --- a/crates/turtle/src/command/client/daemon.rs +++ b/crates/turtle/src/command/client/daemon.rs @@ -22,16 +22,8 @@ use tokio::time::sleep; #[derive(clap::Args, Debug)] pub(crate) struct Cmd { - /// Internal flag for daemonization - #[arg(long, hide = true)] - daemonize: bool, - - /// Also write daemon logs to the console (useful for debugging) - #[arg(long)] - show_logs: bool, - #[command(subcommand)] - subcmd: Option<SubCmd>, + subcmd: SubCmd, } #[derive(Subcommand, Debug)] @@ -67,8 +59,7 @@ impl Cmd { #[cfg(unix)] pub(crate) fn should_daemonize(&self) -> bool { match &self.subcmd { - Some(SubCmd::Start { daemonize, .. }) => *daemonize, - None => self.daemonize, + SubCmd::Start { daemonize, .. } => *daemonize, _ => false, } } @@ -76,8 +67,7 @@ impl Cmd { /// Returns `true` when logs should also be written to the console. pub(crate) fn show_logs(&self) -> bool { match &self.subcmd { - Some(SubCmd::Start { show_logs, .. }) => *show_logs, - None => self.show_logs, + SubCmd::Start { show_logs, .. } => *show_logs, _ => false, } } @@ -89,14 +79,10 @@ impl Cmd { history_db: ClientSqlite, ) -> Result<()> { match self.subcmd { - None => { - eprintln!("Warning: `atuin daemon` is deprecated, use `atuin daemon start`"); - run(settings, store, history_db, false).await - } - Some(SubCmd::Start { force, .. }) => run(settings, store, history_db, force).await, - Some(SubCmd::Status) => status_cmd(&settings).await, - Some(SubCmd::Stop) => stop_cmd(&settings).await, - Some(SubCmd::Restart) => restart_cmd(&settings).await, + SubCmd::Start { force, .. } => run(settings, store, history_db, force).await, + SubCmd::Status => status_cmd(&settings).await, + SubCmd::Stop => stop_cmd(&settings).await, + SubCmd::Restart => restart_cmd(&settings).await, } } } @@ -331,7 +317,6 @@ async fn wait_until_ready(settings: &Settings, timeout: Duration) -> Result<Hist } } -#[expect(clippy::unnecessary_wraps)] fn ensure_autostart_supported(settings: &Settings) -> Result<()> { #[cfg(unix)] if settings.daemon.systemd_socket { @@ -438,7 +423,12 @@ pub(crate) async fn start_history(settings: &Settings, history: History) -> Resu Ok(resp.id) } -pub(crate) async fn end_history(settings: &Settings, id: String, duration: u64, exit: i64) -> Result<()> { +pub(crate) async fn end_history( + settings: &Settings, + id: String, + duration: u64, + exit: i64, +) -> Result<()> { match async { connect_client(settings) .await? diff --git a/crates/turtle/src/command/client/history.rs b/crates/turtle/src/command/client/history.rs index 693098c0..2ddcb3a6 100644 --- a/crates/turtle/src/command/client/history.rs +++ b/crates/turtle/src/command/client/history.rs @@ -10,14 +10,10 @@ use clap::Subcommand; use eyre::{Context, Result, bail}; use runtime_format::{FormatKey, FormatKeyError, ParseSegment, ParsedFmt}; -#[cfg(feature = "daemon")] use super::daemon as daemon_cmd; -#[cfg(feature = "daemon")] use colored::Colorize; -#[cfg(feature = "daemon")] use serde::Serialize; -#[cfg(feature = "daemon")] use crate::atuin_daemon::history::{HistoryEventKind, TailHistoryReply}; use crate::atuin_client::{ @@ -31,13 +27,9 @@ use crate::atuin_client::{ }, }; -#[cfg(feature = "sync")] -use crate::atuin_client::record; - -use log::{debug, warn}; +use log::debug; use time::{OffsetDateTime, macros::format_description}; -#[cfg(feature = "daemon")] use super::daemon; use super::search::format_duration_into; @@ -65,8 +57,10 @@ pub(crate) enum Cmd { /// Finishes a new command in the history (adds time, exit code) End { id: String, + #[arg(long, short)] exit: i64, + #[arg(long, short)] duration: Option<u64>, }, @@ -181,7 +175,6 @@ impl ListMode { } } -#[expect(clippy::cast_sign_loss)] pub(crate) fn print_list( h: &[History], list_mode: ListMode, @@ -410,42 +403,6 @@ fn normalize_command_for_storage<'a>(command: &'a str, settings: &Settings) -> & } } -async fn handle_start( - db: &ClientSqlite, - settings: &Settings, - command: &str, - author: Option<&str>, - intent: Option<&str>, -) -> Result<Option<String>> { - // It's better for atuin to silently fail here and attempt to - // store whatever is ran, than to throw an error to the terminal - let cwd = utils::get_current_dir(); - let command = normalize_command_for_storage(command, settings); - - let mut h: History = History::capture() - .timestamp(OffsetDateTime::now_utc()) - .command(command) - .cwd(cwd) - .build() - .into(); - apply_start_metadata(&mut h, author, intent); - - if !h.should_save(settings) { - return Ok(None); - } - - let id = h.id.0.clone(); - - // Silently ignore database errors to avoid breaking the shell - // This is important when disk is full or database is locked - if let Err(e) = db.save(&h).await { - debug!("failed to save history: {e}"); - } - - Ok(Some(id)) -} - -#[cfg(feature = "daemon")] async fn handle_daemon_start( settings: &Settings, command: &str, @@ -482,65 +439,6 @@ async fn handle_daemon_start( Ok(Some(resp)) } -#[expect(unused_variables)] -async fn handle_end( - db: &ClientSqlite, - store: SqliteStore, - history_store: HistoryStore, - settings: &Settings, - id: &str, - exit: i64, - duration: Option<u64>, -) -> Result<()> { - if id.trim() == "" { - return Ok(()); - } - - let Some(mut h) = db.load(id).await? else { - warn!("history entry is missing"); - return Ok(()); - }; - - if h.duration > 0 { - debug!("cannot end history - already has duration"); - - // returning OK as this can occur if someone Ctrl-c a prompt - return Ok(()); - } - - if !settings.store_failed && exit > 0 { - debug!("history has non-zero exit code, and store_failed is false"); - - // the history has already been inserted half complete. remove it - db.delete(h).await?; - - return Ok(()); - } - - h.exit = exit; - h.duration = match duration { - Some(value) => i64::try_from(value).context("command took over 292 years")?, - None => i64::try_from((OffsetDateTime::now_utc() - h.timestamp).whole_nanoseconds()) - .context("command took over 292 years")?, - }; - - db.update(&h).await?; - history_store.push(h).await?; - - if settings.sync.should_sync().await? { - let (_, downloaded) = - record::sync::sync(settings, &store, &history_store.encryption_key).await?; - Settings::save_sync_time().await?; - - crate::sync::build(settings, &store, db, Some(&downloaded)).await?; - } else { - debug!("sync disabled! not syncing"); - } - - Ok(()) -} - -#[cfg(feature = "daemon")] async fn handle_daemon_end( settings: &Settings, id: &str, @@ -552,70 +450,24 @@ async fn handle_daemon_end( Ok(()) } -pub(super) async fn start_history_entry( - settings: &Settings, - command: &str, - author: Option<&str>, - intent: Option<&str>, -) -> Result<Option<String>> { - #[cfg(feature = "daemon")] - if settings.daemon.enabled { - return handle_daemon_start(settings, command, author, intent).await; - } - - let db_path = PathBuf::from(settings.db_path.as_str()); - let db = ClientSqlite::new(db_path, settings.local_timeout).await?; - handle_start(&db, settings, command, author, intent).await -} - -pub(super) async fn end_history_entry( - settings: &Settings, - id: &str, - exit: i64, - duration: Option<u64>, -) -> Result<()> { - #[cfg(feature = "daemon")] - if settings.daemon.enabled { - return handle_daemon_end(settings, id, exit, duration).await; - } - - let db_path = PathBuf::from(settings.db_path.as_str()); - let record_store_path = PathBuf::from(settings.record_store_path.as_str()); - - let db = ClientSqlite::new(db_path, settings.local_timeout).await?; - let store = SqliteStore::new(record_store_path, settings.local_timeout).await?; - - let encryption_key: [u8; 32] = encryption::load_key(settings) - .context("could not load encryption key")? - .into(); - let host_id = Settings::host_id().await?; - let history_store = HistoryStore::new(store.clone(), host_id, encryption_key); - - handle_end(&db, store, history_store, settings, id, exit, duration).await -} - -#[cfg(feature = "daemon")] #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum TailKind { Started, Ended, } -#[cfg(feature = "daemon")] #[derive(Clone, Debug, Eq, PartialEq)] struct TailEvent { kind: TailKind, history: History, } -#[cfg(feature = "daemon")] #[derive(Serialize)] struct TailJsonEvent<'a> { event: &'static str, history: TailJsonHistory<'a>, } -#[cfg(feature = "daemon")] #[derive(Serialize)] struct TailJsonHistory<'a> { id: &'a str, @@ -642,7 +494,6 @@ struct TailJsonHistory<'a> { finished_at: Option<String>, } -#[cfg(feature = "daemon")] impl TailEvent { fn from_proto(reply: TailHistoryReply) -> Result<Self> { let history = reply @@ -828,7 +679,6 @@ impl TailEvent { } } -#[cfg(feature = "daemon")] impl TailKind { const fn as_str(self) -> &'static str { match self { @@ -846,12 +696,10 @@ impl TailKind { } } -#[cfg(feature = "daemon")] fn format_history_time(timestamp: OffsetDateTime, tz: Timezone) -> Result<String> { Ok(timestamp.to_offset(tz.0).format(TIME_FMT)?) } -#[cfg(feature = "daemon")] fn format_duration_ns(duration_ns: i64) -> String { struct F(Duration); impl Display for F { @@ -863,7 +711,6 @@ fn format_duration_ns(duration_ns: i64) -> String { F(Duration::from_nanos(duration_ns.max(0).cast_unsigned())).to_string() } -#[cfg(feature = "daemon")] fn push_pretty_field(out: &mut String, label: &str, value: &str) { out.push_str(" "); let label = format!("{label}:"); @@ -885,7 +732,6 @@ fn push_pretty_field(out: &mut String, label: &str, value: &str) { } } -#[cfg(feature = "daemon")] fn normalize_optional_field(value: &str) -> Option<String> { let trimmed = value.trim(); if trimmed.is_empty() { @@ -896,7 +742,6 @@ fn normalize_optional_field(value: &str) -> Option<String> { } impl Cmd { - #[cfg(feature = "daemon")] async fn handle_tail(settings: &Settings) -> Result<()> { let tty = std::io::stdout().is_terminal(); let mut client = daemon::tail_client(settings).await?; @@ -918,7 +763,6 @@ impl Cmd { Ok(()) } - #[expect(clippy::too_many_lines, clippy::cast_possible_truncation)] #[expect(clippy::too_many_arguments)] #[expect(clippy::fn_params_excessive_bools)] async fn handle_list( @@ -1010,7 +854,6 @@ impl Cmd { history_store.incremental_build(db, &[id]).await?; } - #[cfg(feature = "daemon")] daemon_cmd::emit_event(settings, crate::atuin_daemon::DaemonEvent::HistoryPruned).await; } Ok(()) @@ -1058,7 +901,6 @@ impl Cmd { let host_id = Settings::host_id().await?; let history_store = HistoryStore::new(store.clone(), host_id, encryption_key); - #[cfg(feature = "daemon")] let ids = matches.iter().map(|h| h.id.clone()).collect::<Vec<_>>(); for entry in matches { @@ -1067,7 +909,6 @@ impl Cmd { history_store.incremental_build(db, &[id]).await?; } - #[cfg(feature = "daemon")] daemon_cmd::emit_event( settings, crate::atuin_daemon::DaemonEvent::HistoryDeleted { ids }, @@ -1093,7 +934,7 @@ impl Cmd { }; if let Some(id) = - start_history_entry(settings, &command, author.as_deref(), intent.as_deref()) + handle_daemon_start(settings, &command, author.as_deref(), intent.as_deref()) .await? { println!("{id}"); @@ -1102,16 +943,10 @@ impl Cmd { Ok(()) } Self::End { id, exit, duration } => { - end_history_entry(settings, &id, exit, duration).await + handle_daemon_end(settings, &id, exit, duration).await } Self::Tail => { - #[cfg(feature = "daemon")] - { - return Self::handle_tail(settings).await; - } - - #[cfg(not(feature = "daemon"))] - bail!("`atuin history tail` requires Atuin to be built with the `daemon` feature"); + return Self::handle_tail(settings).await; } cmd => { let context = current_context().await?; @@ -1204,14 +1039,13 @@ impl Cmd { #[cfg(test)] mod tests { - #[cfg(feature = "daemon")] use time::macros::datetime; use super::*; #[test] fn normalize_command_strips_trailing_spaces_and_tabs() { - let settings = Settings::utc(); + let settings = Settings::new().unwrap(); assert!(settings.strip_trailing_whitespace); assert_eq!(normalize_command_for_storage("ls \t", &settings), "ls"); @@ -1219,7 +1053,7 @@ mod tests { #[test] fn normalize_command_preserves_escaped_trailing_space() { - let settings = Settings::utc(); + let settings = Settings::new().unwrap(); assert_eq!( normalize_command_for_storage("printf foo\\ ", &settings), @@ -1231,45 +1065,6 @@ mod tests { ); } - #[tokio::test] - async fn handle_start_saves_trimmed_command() { - let db = ClientSqlite::new("sqlite::memory:", 2.0).await.unwrap(); - let settings = Settings::utc(); - - handle_start(&db, &settings, "ls \t", None, None) - .await - .unwrap(); - - let history = db - .before(OffsetDateTime::now_utc() + time::Duration::SECOND, 1) - .await - .unwrap() - .pop() - .unwrap(); - assert_eq!(history.command, "ls"); - } - - #[tokio::test] - async fn handle_start_can_keep_trailing_whitespace() { - let db = ClientSqlite::new("sqlite::memory:", 2.0).await.unwrap(); - let settings = Settings { - strip_trailing_whitespace: false, - ..Settings::utc() - }; - - handle_start(&db, &settings, "ls \t", None, None) - .await - .unwrap(); - - let history = db - .before(OffsetDateTime::now_utc() + time::Duration::SECOND, 1) - .await - .unwrap() - .pop() - .unwrap(); - assert_eq!(history.command, "ls \t"); - } - #[test] fn test_format_string_no_panic() { // Don't panic but provide helpful output (issue #2776) @@ -1286,7 +1081,6 @@ mod tests { assert!(std::panic::catch_unwind(|| parse_fmt("{time} - {command}")).is_ok()); } - #[cfg(feature = "daemon")] fn sample_tail_event(kind: TailKind) -> TailEvent { TailEvent { kind, @@ -1306,7 +1100,6 @@ mod tests { } } - #[cfg(feature = "daemon")] #[test] fn test_tail_json_output_contains_history_fields() { let json = sample_tail_event(TailKind::Ended) @@ -1321,7 +1114,6 @@ mod tests { assert!(value.get("record").is_none()); } - #[cfg(feature = "daemon")] #[test] fn test_tail_pretty_output_shows_pending_fields_for_started_events() { let rendered = sample_tail_event(TailKind::Started) diff --git a/crates/turtle/src/command/client/search.rs b/crates/turtle/src/command/client/search.rs index 962e6b1e..bba48b8a 100644 --- a/crates/turtle/src/command/client/search.rs +++ b/crates/turtle/src/command/client/search.rs @@ -12,7 +12,6 @@ use crate::atuin_client::{ history::{History, store::HistoryStore}, record::sqlite_store::SqliteStore, settings::{FilterMode, KeymapMode, SearchMode, Settings, Timezone}, - theme::Theme, }; use super::history::ListMode; @@ -160,7 +159,6 @@ impl Cmd { db: ClientSqlite, settings: &mut Settings, store: SqliteStore, - theme: &Theme, ) -> Result<()> { let query = self.query.unwrap_or_else(|| { std::env::var("ATUIN_QUERY").map_or_else( @@ -226,7 +224,7 @@ impl Cmd { let history_store = HistoryStore::new(store.clone(), host_id, encryption_key); if self.interactive { - let item = interactive::history(&query, settings, db, &history_store, theme).await?; + let item = interactive::history(&query, settings, db, &history_store).await?; if let Some(result_file) = self.result_file { let mut file = File::create(result_file)?; diff --git a/crates/turtle/src/command/client/search/engines.rs b/crates/turtle/src/command/client/search/engines.rs index a84c4798..94834221 100644 --- a/crates/turtle/src/command/client/search/engines.rs +++ b/crates/turtle/src/command/client/search/engines.rs @@ -8,22 +8,14 @@ use eyre::Result; use super::cursor::Cursor; -#[cfg(feature = "daemon")] pub(crate) mod daemon; pub(crate) mod db; pub(crate) mod skim; -#[expect(unused)] // settings is only used if daemon feature is enabled pub(crate) fn engine(search_mode: SearchMode, settings: &Settings) -> Box<dyn SearchEngine> { match search_mode { SearchMode::Skim => Box::new(skim::Search::new()) as Box<_>, - #[cfg(feature = "daemon")] SearchMode::DaemonFuzzy => Box::new(daemon::Search::new(settings)) as Box<_>, - #[cfg(not(feature = "daemon"))] - SearchMode::DaemonFuzzy => { - // Fall back to fuzzy mode if daemon feature is not enabled - Box::new(db::Search(SearchMode::Fuzzy)) as Box<_> - } mode => Box::new(db::Search(mode)) as Box<_>, } } diff --git a/crates/turtle/src/command/client/search/history_list.rs b/crates/turtle/src/command/client/search/history_list.rs index 9d3a60e0..e46f37b7 100644 --- a/crates/turtle/src/command/client/search/history_list.rs +++ b/crates/turtle/src/command/client/search/history_list.rs @@ -5,7 +5,10 @@ use super::engines::SearchEngine; use crate::atuin_client::{ history::History, settings::{UiColumn, UiColumnType}, - theme::{Meaning, Theme}, + theme::{ + style_alerterror, style_alertinfo, style_alertwarn, style_annotation, style_base, + style_guidance, + }, }; use crate::atuin_common::utils::Escapable as _; use itertools::Itertools; @@ -39,7 +42,7 @@ pub(crate) struct HistoryList<'a> { alternate_highlight: bool, now: &'a dyn Fn() -> OffsetDateTime, indicator: &'a str, - theme: &'a Theme, + history_highlighter: HistoryHighlighter<'a>, show_numeric_shortcuts: bool, /// Columns to display (in order, after the indicator) @@ -100,7 +103,7 @@ impl StatefulWidget for HistoryList<'_> { alternate_highlight: self.alternate_highlight, now: &self.now, indicator: self.indicator, - theme: self.theme, + history_highlighter: self.history_highlighter, show_numeric_shortcuts: self.show_numeric_shortcuts, columns: self.columns, @@ -124,7 +127,7 @@ impl<'a> HistoryList<'a> { alternate_highlight: bool, now: &'a dyn Fn() -> OffsetDateTime, indicator: &'a str, - theme: &'a Theme, + history_highlighter: HistoryHighlighter<'a>, show_numeric_shortcuts: bool, columns: &'a [UiColumn], @@ -136,7 +139,6 @@ impl<'a> HistoryList<'a> { alternate_highlight, now, indicator, - theme, history_highlighter, show_numeric_shortcuts, columns, @@ -173,7 +175,7 @@ struct DrawState<'a> { alternate_highlight: bool, now: &'a dyn Fn() -> OffsetDateTime, indicator: &'a str, - theme: &'a Theme, + history_highlighter: HistoryHighlighter<'a>, show_numeric_shortcuts: bool, columns: &'a [UiColumn], @@ -203,7 +205,7 @@ impl DrawState<'_> { .width .saturating_sub(indicator_width + fixed_width); - let style = self.theme.as_style(Meaning::Base); + let style = style_base(); // Render each configured column for (idx, column) in self.columns.iter().enumerate() { if idx != 0 { @@ -251,11 +253,11 @@ impl DrawState<'_> { } fn duration(&mut self, h: &History, width: u16) { - let style = self.theme.as_style(if h.success() { - Meaning::AlertInfo + let style = if h.success() { + style_alertinfo() } else { - Meaning::AlertError - }); + style_alerterror() + }; let duration = Duration::from_nanos(u64::try_from(h.duration).unwrap_or(0)); let formatted = format_duration(duration); let w = width as usize; @@ -265,7 +267,7 @@ impl DrawState<'_> { } fn time(&mut self, h: &History, width: u16) { - let style = self.theme.as_style(Meaning::Guidance); + let style = style_guidance(); // Account for the chance that h.timestamp is "in the future" // This would mean that "since" is negative, and the unwrap here @@ -284,13 +286,13 @@ impl DrawState<'_> { } fn command(&mut self, h: &History) { - let mut style = self.theme.as_style(Meaning::Base); + let mut style = style_base(); let mut row_highlighted = false; if !self.alternate_highlight && (self.y as usize + self.state.offset == self.state.selected) { row_highlighted = true; // if not applying alternative highlighting to the whole row, color the command - style = self.theme.as_style(Meaning::AlertError); + style = style_alerterror(); style.attributes.set(style::Attribute::Bold); } @@ -318,7 +320,7 @@ impl DrawState<'_> { if row_highlighted { // if the row is highlighted bold is not enough as the whole row is bold // change the color too - style = self.theme.as_style(Meaning::AlertWarn); + style = style_alertwarn(); } style.attributes.set(style::Attribute::Bold); } @@ -332,7 +334,7 @@ impl DrawState<'_> { /// Render the absolute datetime column (e.g., "2025-01-22 14:35") fn datetime(&mut self, h: &History, width: u16) { - let style = self.theme.as_style(Meaning::Annotation); + let style = style_annotation(); // Format: YYYY-MM-DD HH:MM let formatted = h .timestamp @@ -348,7 +350,7 @@ impl DrawState<'_> { /// Render the directory column (working directory, truncated) fn directory(&mut self, h: &History, width: u16) { - let style = self.theme.as_style(Meaning::Annotation); + let style = style_annotation(); let w = width as usize; let cwd = &h.cwd; let char_count = cwd.chars().count(); @@ -365,7 +367,7 @@ impl DrawState<'_> { /// Render the host column (just the hostname) fn host(&mut self, h: &History, width: u16) { - let style = self.theme.as_style(Meaning::Annotation); + let style = style_annotation(); let w = width as usize; // Database stores hostname as "hostname:username" let host = h.hostname.split(':').next().unwrap_or(&h.hostname); @@ -382,7 +384,7 @@ impl DrawState<'_> { /// Render the user column fn user(&mut self, h: &History, width: u16) { - let style = self.theme.as_style(Meaning::Annotation); + let style = style_annotation(); let w = width as usize; // Database stores hostname as "hostname:username" let user = h.hostname.split(':').nth(1).unwrap_or(""); @@ -400,9 +402,9 @@ impl DrawState<'_> { /// Render the exit code column fn exit_code(&mut self, h: &History, width: u16) { let style = if h.success() { - self.theme.as_style(Meaning::AlertInfo) + style_alertinfo() } else { - self.theme.as_style(Meaning::AlertError) + style_alerterror() }; let w = width as usize; let display = format!("{:>w$}", h.exit); diff --git a/crates/turtle/src/command/client/search/inspector.rs b/crates/turtle/src/command/client/search/inspector.rs index a1bf803f..f7b40a26 100644 --- a/crates/turtle/src/command/client/search/inspector.rs +++ b/crates/turtle/src/command/client/search/inspector.rs @@ -4,6 +4,7 @@ use time::macros::format_description; use crate::atuin_client::{ history::{History, HistoryStats}, settings::{Settings, Timezone}, + theme::{style_annotation, style_base, style_important}, }; use ratatui::{ Frame, @@ -17,7 +18,6 @@ use ratatui::{ use super::duration::format_duration; -use super::super::theme::{Meaning, Theme}; use super::interactive::{Compactness, to_compactness}; #[expect(clippy::cast_sign_loss)] @@ -31,7 +31,6 @@ pub(crate) fn draw_commands( history: &History, stats: &HistoryStats, compact: bool, - theme: &Theme, ) { let commands = Layout::default() .direction(if compact { @@ -56,16 +55,16 @@ pub(crate) fn draw_commands( let command = Paragraph::new(Text::from(Span::styled( history.command.clone(), - Style::from_crossterm(theme.as_style(Meaning::Important)), + Style::from_crossterm(style_important()), ))) .block(if compact { Block::new() .borders(Borders::NONE) - .style(Style::from_crossterm(theme.as_style(Meaning::Base))) + .style(Style::from_crossterm(style_base())) } else { Block::new() .borders(Borders::ALL) - .style(Style::from_crossterm(theme.as_style(Meaning::Base))) + .style(Style::from_crossterm(style_base())) .title("Command") .padding(Padding::horizontal(1)) }); @@ -79,11 +78,11 @@ pub(crate) fn draw_commands( .block(if compact { Block::new() .borders(Borders::NONE) - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))) + .style(Style::from_crossterm(style_annotation())) } else { Block::new() .borders(Borders::ALL) - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))) + .style(Style::from_crossterm(style_annotation())) .title("Previous command") .padding(Padding::horizontal(1)) }); @@ -99,13 +98,13 @@ pub(crate) fn draw_commands( .block(if compact { Block::new() .borders(Borders::NONE) - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))) + .style(Style::from_crossterm(style_annotation())) } else { Block::new() .borders(Borders::ALL) .title("Next command") .padding(Padding::horizontal(1)) - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))) + .style(Style::from_crossterm(style_annotation())) }); f.render_widget(previous, commands[0]); @@ -119,7 +118,6 @@ pub(crate) fn draw_stats_table( history: &History, tz: Timezone, stats: &HistoryStats, - theme: &Theme, ) { let duration = Duration::from_nanos(u64_or_zero(history.duration)); let avg_duration = Duration::from_nanos(stats.average_duration); @@ -149,7 +147,7 @@ pub(crate) fn draw_stats_table( Block::default() .title("Command stats") .borders(Borders::ALL) - .style(Style::from_crossterm(theme.as_style(Meaning::Base))) + .style(Style::from_crossterm(style_base())) .padding(Padding::vertical(1)), ); @@ -196,7 +194,7 @@ fn sort_duration_over_time(durations: &[(String, i64)]) -> Vec<(String, i64)> { .collect() } -fn draw_stats_charts(f: &mut Frame<'_>, parent: Rect, stats: &HistoryStats, theme: &Theme) { +fn draw_stats_charts(f: &mut Frame<'_>, parent: Rect, stats: &HistoryStats) { let exits: Vec<Bar> = stats .exits .iter() @@ -211,7 +209,7 @@ fn draw_stats_charts(f: &mut Frame<'_>, parent: Rect, stats: &HistoryStats, them .block( Block::default() .title("Exit code distribution") - .style(Style::from_crossterm(theme.as_style(Meaning::Base))) + .style(Style::from_crossterm(style_base())) .borders(Borders::ALL), ) .bar_width(3) @@ -235,7 +233,7 @@ fn draw_stats_charts(f: &mut Frame<'_>, parent: Rect, stats: &HistoryStats, them .block( Block::default() .title("Runs per day") - .style(Style::from_crossterm(theme.as_style(Meaning::Base))) + .style(Style::from_crossterm(style_base())) .borders(Borders::ALL), ) .bar_width(3) @@ -261,7 +259,7 @@ fn draw_stats_charts(f: &mut Frame<'_>, parent: Rect, stats: &HistoryStats, them .block( Block::default() .title("Duration over time") - .style(Style::from_crossterm(theme.as_style(Meaning::Base))) + .style(Style::from_crossterm(style_base())) .borders(Borders::ALL), ) .bar_width(5) @@ -291,14 +289,13 @@ pub(crate) fn draw( history: &History, stats: &HistoryStats, settings: &Settings, - theme: &Theme, tz: Timezone, ) { let compactness = to_compactness(f, settings); match compactness { - Compactness::Ultracompact => draw_ultracompact(f, chunk, history, stats, theme), - _ => draw_full(f, chunk, history, stats, theme, tz), + Compactness::Ultracompact => draw_ultracompact(f, chunk, history, stats), + _ => draw_full(f, chunk, history, stats, tz), } } @@ -307,9 +304,8 @@ pub(crate) fn draw_ultracompact( chunk: Rect, history: &History, stats: &HistoryStats, - theme: &Theme, ) { - draw_commands(f, chunk, history, stats, true, theme); + draw_commands(f, chunk, history, stats, true); } pub(crate) fn draw_full( @@ -317,7 +313,6 @@ pub(crate) fn draw_full( chunk: Rect, history: &History, stats: &HistoryStats, - theme: &Theme, tz: Timezone, ) { let vert_layout = Layout::default() @@ -330,18 +325,15 @@ pub(crate) fn draw_full( .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)]) .split(vert_layout[1]); - draw_commands(f, vert_layout[0], history, stats, false, theme); - draw_stats_table(f, stats_layout[0], history, tz, stats, theme); - draw_stats_charts(f, stats_layout[1], stats, theme); + draw_commands(f, vert_layout[0], history, stats, false); + draw_stats_table(f, stats_layout[0], history, tz, stats); + draw_stats_charts(f, stats_layout[1], stats); } #[cfg(test)] mod tests { use super::draw_ultracompact; - use crate::atuin_client::{ - history::{History, HistoryId, HistoryStats}, - theme::ThemeManager, - }; + use crate::atuin_client::history::{History, HistoryId, HistoryStats}; use ratatui::{backend::TestBackend, prelude::*}; use time::OffsetDateTime; @@ -406,9 +398,7 @@ mod tests { let prev = stats.previous.clone().unwrap(); let next = stats.next.clone().unwrap(); - let mut manager = ThemeManager::new(Some(true), Some("".to_string())); - let theme = manager.load_theme("(none)", None); - let _ = terminal.draw(|f| draw_ultracompact(f, chunk, &history, &stats, &theme)); + let _ = terminal.draw(|f| draw_ultracompact(f, chunk, &history, &stats)); let mut lines = [" "; 5].map(|l| Line::from(l)); for (n, entry) in [prev, history, next].iter().enumerate() { let mut l = lines[n].to_string(); diff --git a/crates/turtle/src/command/client/search/interactive.rs b/crates/turtle/src/command/client/search/interactive.rs index 1d067e50..2c6af8cf 100644 --- a/crates/turtle/src/command/client/search/interactive.rs +++ b/crates/turtle/src/command/client/search/interactive.rs @@ -7,7 +7,10 @@ use std::{ use std::io::Read as _; use crate::{ - atuin_client::database::ClientSqlite, + atuin_client::{ + database::ClientSqlite, + theme::{style_annotation, style_base, style_important}, + }, atuin_common::{shell::Shell, utils::Escapable as _}, }; use eyre::Result; @@ -30,7 +33,6 @@ use crate::atuin_client::{ use crate::command::client::search::history_list::HistoryHighlighter; use crate::command::client::search::keybindings::KeymapSet; -use crate::command::client::theme::{Meaning, Theme}; use crate::{VERSION, command::client::search::engines}; use ratatui::{ @@ -42,7 +44,7 @@ use ratatui::{ execute, queue, terminal, }, layout::{Alignment, Constraint, Direction, Layout}, - prelude::*, + prelude::Rect, style::{Modifier, Style}, text::{Line, Span, Text}, widgets::{Block, BorderType, Borders, Clear, Padding, Paragraph, Tabs}, @@ -800,9 +802,6 @@ impl State { } } - #[expect(clippy::bool_to_int_with_if)] - #[expect(clippy::too_many_lines)] - #[expect(clippy::too_many_arguments)] fn draw( &mut self, f: &mut Frame, @@ -810,17 +809,16 @@ impl State { stats: Option<HistoryStats>, inspecting: Option<&History>, settings: &Settings, - theme: &Theme, + popup_mode: bool, ) { let area = f.area(); if popup_mode { f.render_widget(Clear, area); } - self.draw_inner(f, area, results, stats, inspecting, settings, theme); + self.draw_inner(f, area, results, stats, inspecting, settings); } - #[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_lines)] #[expect(clippy::bool_to_int_with_if)] fn draw_inner( @@ -831,7 +829,6 @@ impl State { stats: Option<HistoryStats>, inspecting: Option<&History>, settings: &Settings, - theme: &Theme, ) { let compactness = to_compactness(f, settings); let invert = settings.invert; @@ -905,7 +902,7 @@ impl State { .block(Block::default().borders(Borders::NONE)) .select(self.tab_index) .style(Style::default()) - .highlight_style(Style::from_crossterm(theme.as_style(Meaning::Important))); + .highlight_style(Style::from_crossterm(style_important())); f.render_widget(tabs, tabs_chunk); } @@ -928,13 +925,13 @@ impl State { ) .split(header_chunk); - let title = Self::build_title(theme); + let title = Self::build_title(); f.render_widget(title, header_chunks[0]); - let help = self.build_help(settings, theme); + let help = self.build_help(settings); f.render_widget(help, header_chunks[1]); - let stats_tab = self.build_stats(theme); + let stats_tab = self.build_stats(); f.render_widget(stats_tab, header_chunks[2]); let indicator: String = match compactness { @@ -968,7 +965,6 @@ impl State { self.keymap_mode, &self.now, indicator.as_str(), - theme, history_highlighter, settings.show_numeric_shortcuts, &settings.ui.columns, @@ -999,7 +995,6 @@ impl State { inspecting, &stats.expect("Drawing inspector, but no stats"), settings, - theme, settings.timezone, ); } @@ -1029,7 +1024,6 @@ impl State { compactness, preview_width, preview_chunk.width.into(), - theme, ); #[expect(clippy::cast_possible_truncation)] let prefix_width = settings @@ -1083,9 +1077,9 @@ impl State { )); } - fn build_title(theme: &Theme) -> Paragraph<'_> { + fn build_title<'a>() -> Paragraph<'a> { let title = { - let style: Style = Style::from_crossterm(theme.as_style(Meaning::Base)); + let style: Style = Style::from_crossterm(style_base()); Paragraph::new(Text::from(Span::styled( format!("Atuin v{VERSION}"), style.add_modifier(Modifier::BOLD), @@ -1095,7 +1089,7 @@ impl State { } #[expect(clippy::unused_self)] - fn build_help(&self, settings: &Settings, theme: &Theme) -> Paragraph<'_> { + fn build_help(&self, settings: &Settings) -> Paragraph<'_> { match self.tab_index { // search 0 => Paragraph::new(Text::from(Line::from(vec![ @@ -1129,16 +1123,16 @@ impl State { _ => unreachable!("invalid tab index"), } - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))) + .style(Style::from_crossterm(style_annotation())) .alignment(Alignment::Center) } - fn build_stats(&self, theme: &Theme) -> Paragraph<'_> { + fn build_stats(&self) -> Paragraph<'_> { Paragraph::new(Text::from(Span::raw(format!( "history count: {}", self.history_count, )))) - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))) + .style(Style::from_crossterm(style_annotation())) .alignment(Alignment::Right) } @@ -1149,7 +1143,7 @@ impl State { keymap_mode: KeymapMode, now: &'a dyn Fn() -> OffsetDateTime, indicator: &'a str, - theme: &'a Theme, + history_highlighter: HistoryHighlighter<'a>, show_numeric_shortcuts: bool, columns: &'a [UiColumn], @@ -1160,7 +1154,6 @@ impl State { keymap_mode == KeymapMode::VimNormal, now, indicator, - theme, history_highlighter, show_numeric_shortcuts, columns, @@ -1228,7 +1221,6 @@ impl State { compactness: Compactness, preview_width: u16, chunk_width: usize, - theme: &Theme, ) -> Paragraph<'_> { let selected = self.results_state.selected(); let command = if results.is_empty() { @@ -1264,8 +1256,7 @@ impl State { .border_type(BorderType::Rounded) .title(format!("{:─>width$}", "", width = chunk_width - 2)), ), - _ => Paragraph::new(command) - .style(Style::from_crossterm(theme.as_style(Meaning::Annotation))), + _ => Paragraph::new(command).style(Style::from_crossterm(style_annotation())), } } } @@ -1320,9 +1311,7 @@ impl Write for TerminalWriter { /// Screen state captured from atuin pty-proxy's screen server. #[cfg(unix)] struct SavedScreen { - #[expect(dead_code)] rows: u16, - #[expect(dead_code)] cols: u16, cursor_row: u16, cursor_col: u16, @@ -1555,7 +1544,6 @@ pub(crate) async fn history( settings: &Settings, mut db: ClientSqlite, history_store: &HistoryStore, - theme: &Theme, ) -> Result<String> { let inline_height = if settings.shell_up_key_binding { settings @@ -1752,7 +1740,6 @@ pub(crate) async fn history( stats.clone(), inspecting.as_ref(), settings, - theme, popup_mode, ); })?; @@ -1832,7 +1819,7 @@ pub(crate) async fn history( terminal.clear()?; } terminal.draw(|f| { - app.draw(f, &results, stats.clone(), inspecting.as_ref(), settings, theme, popup_mode); + app.draw(f, &results, stats.clone(), inspecting.as_ref(), settings, popup_mode); })?; }, r => { @@ -1979,10 +1966,7 @@ pub(crate) async fn history( // cli-clipboard only works on Windows, Mac, and Linux. -#[cfg(all( - feature = "clipboard", - any(target_os = "windows", target_os = "macos", target_os = "linux") -))] +#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] fn set_clipboard(s: String) { let mut ctx = arboard::Clipboard::new().unwrap(); ctx.set_text(s).unwrap(); @@ -1990,12 +1974,6 @@ fn set_clipboard(s: String) { ctx.get_text().unwrap(); } -#[cfg(not(all( - feature = "clipboard", - any(target_os = "windows", target_os = "macos", target_os = "linux") -)))] -fn set_clipboard(_s: String) {} - #[cfg(test)] mod tests { use crate::atuin_client::database::Context; @@ -2018,7 +1996,7 @@ mod tests { strategy: PreviewStrategy::Auto, }, show_preview: true, - ..Settings::utc() + ..Settings::now() }; let settings_preview_auto_h2 = Settings { @@ -2027,7 +2005,7 @@ mod tests { }, show_preview: true, max_preview_height: 2, - ..Settings::utc() + ..Settings::now() }; let settings_preview_h4 = Settings { @@ -2036,7 +2014,7 @@ mod tests { }, show_preview: true, max_preview_height: 4, - ..Settings::utc() + ..Settings::now() }; let settings_preview_fixed = Settings { @@ -2045,7 +2023,7 @@ mod tests { }, show_preview: true, max_preview_height: 15, - ..Settings::utc() + ..Settings::now() }; let cmd_60: History = History::capture() @@ -2167,7 +2145,7 @@ mod tests { // Test when there's no results, scrolling up or down doesn't underflow #[test] fn state_scroll_up_underflow() { - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: 0, results_state: ListState::default(), @@ -2212,7 +2190,7 @@ mod tests { use crate::atuin_client::settings::Keys; use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - let mut settings = Settings::utc(); + let mut settings = Settings::now(); settings.keys = Keys { scroll_exits: true, exit_past_line_start: false, @@ -2338,7 +2316,7 @@ mod tests { fn test_vim_gg_multikey_sequence() { use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: 100, @@ -2396,7 +2374,7 @@ mod tests { fn test_vim_g_key_clears_on_other_input() { use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: 100, @@ -2450,7 +2428,7 @@ mod tests { fn test_vim_big_g_jump_to_bottom() { use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: 100, @@ -2500,7 +2478,7 @@ mod tests { fn test_vim_ctrl_u_d_half_page_scroll() { use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: 100, @@ -2559,7 +2537,7 @@ mod tests { fn test_vim_ctrl_f_b_full_page_scroll() { use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: 100, @@ -2620,7 +2598,7 @@ mod tests { /// Helper to build a State for executor tests. fn make_executor_state(results_len: usize, selected: usize) -> State { - let settings = Settings::utc(); + let settings = Settings::now(); let mut state = State { history_count: results_len as i64, results_state: ListState::default(), @@ -2664,7 +2642,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::SelectNext, &settings); assert!(matches!(result, super::InputAction::Continue)); // Non-inverted: SelectNext = scroll_down = selected - 1 @@ -2676,7 +2654,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let mut settings = Settings::utc(); + let mut settings = Settings::now(); settings.invert = true; let result = state.execute_action(&Action::SelectNext, &settings); assert!(matches!(result, super::InputAction::Continue)); @@ -2689,7 +2667,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::SelectPrevious, &settings); assert!(matches!(result, super::InputAction::Continue)); // Non-inverted: SelectPrevious = scroll_up = selected + 1 @@ -2701,7 +2679,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::VimEnterNormal, &settings); assert!(matches!(result, super::InputAction::Continue)); assert_eq!(state.keymap_mode, KeymapMode::VimNormal); @@ -2713,7 +2691,7 @@ mod tests { let mut state = make_executor_state(100, 0); state.keymap_mode = KeymapMode::VimNormal; - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::VimEnterInsert, &settings); assert!(matches!(result, super::InputAction::Continue)); assert_eq!(state.keymap_mode, KeymapMode::VimInsert); @@ -2724,7 +2702,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 5); - let mut settings = Settings::utc(); + let mut settings = Settings::now(); settings.enter_accept = true; let result = state.execute_action(&Action::Accept, &settings); assert!(matches!(result, super::InputAction::Accept(5))); @@ -2736,7 +2714,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 5); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::ReturnSelection, &settings); assert!(matches!(result, super::InputAction::Accept(5))); assert!(!state.accept); @@ -2747,7 +2725,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 5); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::AcceptNth(3), &settings); assert!(matches!(result, super::InputAction::Accept(8))); } @@ -2757,7 +2735,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::ScrollToTop, &settings); assert!(matches!(result, super::InputAction::Continue)); // Non-inverted: visual top = highest index @@ -2769,7 +2747,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let mut settings = Settings::utc(); + let mut settings = Settings::now(); settings.invert = true; let result = state.execute_action(&Action::ScrollToTop, &settings); assert!(matches!(result, super::InputAction::Continue)); @@ -2782,7 +2760,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::ScrollToBottom, &settings); assert!(matches!(result, super::InputAction::Continue)); // Non-inverted: visual bottom = index 0 @@ -2794,7 +2772,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); assert_eq!(state.tab_index, 0); state.execute_action(&Action::ToggleTab, &settings); assert_eq!(state.tab_index, 1); @@ -2807,7 +2785,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); assert!(!state.prefix); state.execute_action(&Action::EnterPrefixMode, &settings); assert!(state.prefix); @@ -2819,7 +2797,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let mut settings = Settings::utc(); + let mut settings = Settings::now(); settings.exit_mode = ExitMode::ReturnOriginal; let result = state.execute_action(&Action::Exit, &settings); @@ -2835,7 +2813,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::ReturnOriginal, &settings); assert!(matches!(result, super::InputAction::ReturnOriginal)); } @@ -2845,7 +2823,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 7); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::Copy, &settings); assert!(matches!(result, super::InputAction::Copy(7))); } @@ -2855,7 +2833,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 7); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::Delete, &settings); assert!(matches!(result, super::InputAction::Delete(7))); } @@ -2865,7 +2843,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 7); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::SwitchContext, &settings); assert!(matches!(result, super::InputAction::SwitchContext(Some(7)))); } @@ -2875,7 +2853,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 7); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::ClearContext, &settings); assert!(matches!(result, super::InputAction::SwitchContext(None))); } @@ -2885,7 +2863,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 50); - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::Noop, &settings); assert!(matches!(result, super::InputAction::Continue)); assert_eq!(state.results_state.selected(), 50); @@ -2897,7 +2875,7 @@ mod tests { let mut state = make_executor_state(100, 5); state.tab_index = 1; - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::Accept, &settings); assert!(matches!(result, super::InputAction::AcceptInspecting)); } @@ -2907,7 +2885,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); let original_mode = state.search_mode; let result = state.execute_action(&Action::CycleSearchMode, &settings); assert!(matches!(result, super::InputAction::Continue)); @@ -2923,7 +2901,7 @@ mod tests { state.search.input.insert('h'); state.search.input.insert('i'); state.keymap_mode = KeymapMode::VimNormal; - let settings = Settings::utc(); + let settings = Settings::now(); let result = state.execute_action(&Action::VimSearchInsert, &settings); assert!(matches!(result, super::InputAction::Continue)); // Should clear input and switch to insert mode @@ -2936,7 +2914,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); // Insert some text state.search.input.insert('h'); @@ -2968,7 +2946,7 @@ mod tests { use crate::command::client::search::keybindings::Action; let mut state = make_executor_state(100, 0); - let settings = Settings::utc(); + let settings = Settings::now(); // Insert "hello" state.search.input.insert('h'); @@ -2992,7 +2970,7 @@ mod tests { use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use std::collections::HashMap; - let mut settings = Settings::utc(); + let mut settings = Settings::now(); // Configure tab to return-query settings.keymap.emacs = HashMap::from([( "tab".to_string(), diff --git a/crates/turtle/src/command/client/stats.rs b/crates/turtle/src/command/client/stats.rs index 17432bb2..9ea5e283 100644 --- a/crates/turtle/src/command/client/stats.rs +++ b/crates/turtle/src/command/client/stats.rs @@ -4,7 +4,7 @@ use interim::parse_date_string; use time::{Duration, OffsetDateTime, Time}; use crate::atuin_client::database::ClientSqlite; -use crate::atuin_client::{database::current_context, settings::Settings, theme::Theme}; +use crate::atuin_client::{database::current_context, settings::Settings}; use crate::atuin_history::stats::{compute, pretty_print}; @@ -36,12 +36,7 @@ pub(crate) struct Cmd { } impl Cmd { - pub(crate) async fn run( - &self, - db: &ClientSqlite, - settings: &Settings, - theme: &Theme, - ) -> Result<()> { + pub(crate) async fn run(&self, db: &ClientSqlite, settings: &Settings) -> Result<()> { let context = current_context().await?; let words = if self.period.is_empty() { String::from("all") @@ -79,7 +74,7 @@ impl Cmd { let stats = compute(settings, &history, self.count, self.ngram_size); if let Some(stats) = stats { - pretty_print(stats, self.ngram_size, theme); + pretty_print(stats, self.ngram_size); } Ok(()) diff --git a/crates/turtle/src/command/client/store.rs b/crates/turtle/src/command/client/store.rs index 347c4bee..bc57488d 100644 --- a/crates/turtle/src/command/client/store.rs +++ b/crates/turtle/src/command/client/store.rs @@ -2,18 +2,14 @@ use clap::Subcommand; use eyre::Result; use crate::atuin_client::{ - database::ClientSqlite, record::{sqlite_store::SqliteStore, store::Store}, settings::Settings + database::ClientSqlite, record::sqlite_store::SqliteStore, settings::Settings, }; use itertools::Itertools; use time::{OffsetDateTime, UtcOffset}; -#[cfg(feature = "sync")] -mod push; - -#[cfg(feature = "sync")] mod pull; - mod purge; +mod push; mod rebuild; mod rekey; mod verify; @@ -37,11 +33,9 @@ pub(crate) enum Cmd { Verify(verify::Verify), /// Push all records to the remote sync server (one way sync) - #[cfg(feature = "sync")] Push(push::Push), /// Pull records from the remote sync server (one way sync) - #[cfg(feature = "sync")] Pull(pull::Pull), } @@ -58,11 +52,7 @@ impl Cmd { Self::Rekey(rekey) => rekey.run(settings, store).await, Self::Verify(verify) => verify.run(settings, store).await, Self::Purge(purge) => purge.run(settings, store).await, - - #[cfg(feature = "sync")] Self::Push(push) => push.run(settings, store).await, - - #[cfg(feature = "sync")] Self::Pull(pull) => pull.run(settings, store, database).await, } } diff --git a/crates/turtle/src/command/client/store/pull.rs b/crates/turtle/src/command/client/store/pull.rs index f2e628d6..3a0865be 100644 --- a/crates/turtle/src/command/client/store/pull.rs +++ b/crates/turtle/src/command/client/store/pull.rs @@ -2,7 +2,13 @@ use clap::Args; use eyre::Result; use crate::atuin_client::{ - database::ClientSqlite, encryption::load_key, record::{sqlite_store::SqliteStore, store::Store, sync::{self, Operation}}, settings::Settings + database::ClientSqlite, + encryption::load_key, + record::{ + sqlite_store::SqliteStore, + sync::{self, Operation}, + }, + settings::Settings, }; #[derive(Args, Debug)] @@ -42,7 +48,7 @@ impl Pull { // 3. Filter operations by // a) are they a download op? // b) are they for the host/tag we are pushing here? - let client = sync::build_client(settings).await?; + let client = sync::build_client(settings)?; let (diff, remote_index) = sync::diff(&client, &store).await?; // Skip on --force: local was already wiped above, mismatch is the user's call. @@ -53,7 +59,7 @@ impl Pull { .map_err(crate::print_error::format_sync_error)?; } - let operations = sync::operations(diff, &store).await?; + let operations = sync::operations(diff, &store)?; let operations = operations .into_iter() diff --git a/crates/turtle/src/command/client/store/purge.rs b/crates/turtle/src/command/client/store/purge.rs index 3ed55787..a23f1886 100644 --- a/crates/turtle/src/command/client/store/purge.rs +++ b/crates/turtle/src/command/client/store/purge.rs @@ -2,9 +2,7 @@ use clap::Args; use eyre::Result; use crate::atuin_client::{ - encryption::load_key, - record::{sqlite_store::SqliteStore, store::Store}, - settings::Settings, + encryption::load_key, record::sqlite_store::SqliteStore, settings::Settings, }; #[derive(Args, Debug)] diff --git a/crates/turtle/src/command/client/store/push.rs b/crates/turtle/src/command/client/store/push.rs index beec613c..9d66b5b2 100644 --- a/crates/turtle/src/command/client/store/push.rs +++ b/crates/turtle/src/command/client/store/push.rs @@ -60,7 +60,7 @@ impl Push { // 3. Filter operations by // a) are they an upload op? // b) are they for the host/tag we are pushing here? - let client = sync::build_client(settings).await?; + let client = sync::build_client(settings)?; let (diff, remote_index) = sync::diff(&client, &store).await?; // Skip on --force: that path intentionally replaces remote with local. @@ -71,7 +71,7 @@ impl Push { .map_err(crate::print_error::format_sync_error)?; } - let operations = sync::operations(diff, &store).await?; + let operations = sync::operations(diff, &store)?; let operations = operations .into_iter() diff --git a/crates/turtle/src/command/client/store/rebuild.rs b/crates/turtle/src/command/client/store/rebuild.rs index bee1aa05..6be67cd0 100644 --- a/crates/turtle/src/command/client/store/rebuild.rs +++ b/crates/turtle/src/command/client/store/rebuild.rs @@ -1,7 +1,6 @@ use clap::Args; use eyre::{Result, bail}; -#[cfg(feature = "daemon")] use crate::command::client::daemon as daemon_cmd; use crate::atuin_client::{ @@ -50,7 +49,6 @@ impl Rebuild { history_store.build(database).await?; - #[cfg(feature = "daemon")] daemon_cmd::emit_event(settings, crate::atuin_daemon::DaemonEvent::HistoryRebuilt).await; Ok(()) diff --git a/crates/turtle/src/command/client/store/rekey.rs b/crates/turtle/src/command/client/store/rekey.rs index b99fb16a..e89d83c2 100644 --- a/crates/turtle/src/command/client/store/rekey.rs +++ b/crates/turtle/src/command/client/store/rekey.rs @@ -5,7 +5,6 @@ use tokio::{fs::File, io::AsyncWriteExt}; use crate::atuin_client::{ encryption::{decode_key, generate_encoded_key, load_key}, record::sqlite_store::SqliteStore, - record::store::Store, settings::Settings, }; diff --git a/crates/turtle/src/command/client/store/verify.rs b/crates/turtle/src/command/client/store/verify.rs index e91addcf..a39227f9 100644 --- a/crates/turtle/src/command/client/store/verify.rs +++ b/crates/turtle/src/command/client/store/verify.rs @@ -2,9 +2,7 @@ use clap::Args; use eyre::Result; use crate::atuin_client::{ - encryption::load_key, - record::{sqlite_store::SqliteStore, store::Store}, - settings::Settings, + encryption::load_key, record::sqlite_store::SqliteStore, settings::Settings, }; #[derive(Args, Debug)] diff --git a/crates/turtle/src/command/client/sync.rs b/crates/turtle/src/command/client/sync.rs index 84b74cc1..c29a82fc 100644 --- a/crates/turtle/src/command/client/sync.rs +++ b/crates/turtle/src/command/client/sync.rs @@ -4,7 +4,11 @@ use serde_json::json; use crate::{ atuin_client::{ - database::ClientSqlite, encryption, history::store::HistoryStore, record::{sqlite_store::SqliteStore, store::Store, sync}, settings::Settings + database::ClientSqlite, + encryption, + history::store::HistoryStore, + record::{sqlite_store::SqliteStore, sync}, + settings::Settings, }, atuin_common::utils, }; diff --git a/crates/turtle/src/command/client/wrapped.rs b/crates/turtle/src/command/client/wrapped.rs index d502d3ec..2ce19bf7 100644 --- a/crates/turtle/src/command/client/wrapped.rs +++ b/crates/turtle/src/command/client/wrapped.rs @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet}; use time::{Date, Duration, Month, OffsetDateTime, Time}; use crate::atuin_client::database::ClientSqlite; -use crate::atuin_client::{settings::Settings, theme::Theme}; +use crate::atuin_client::settings::Settings; use crate::atuin_history::stats::{Stats, compute}; @@ -31,7 +31,12 @@ impl WrappedStats { .iter() .filter(|(cmd, _)| { let cmd = &cmd[0]; - cmd == "cd" || cmd == "ls" || cmd == "pwd" || cmd == "pushd" || cmd == "popd" + cmd == "cd" + || cmd == "ls" + || cmd == "ll" + || cmd == "pwd" + || cmd == "pushd" + || cmd == "popd" }) .map(|(_, count)| count) .sum(); @@ -267,12 +272,7 @@ fn print_fun_facts(wrapped_stats: &WrappedStats, stats: &Stats, year: i32) { println!(); } -pub(crate) async fn run( - year: Option<i32>, - db: &ClientSqlite, - settings: &Settings, - theme: &Theme, -) -> Result<()> { +pub(crate) async fn run(year: Option<i32>, db: &ClientSqlite, settings: &Settings) -> Result<()> { let now = OffsetDateTime::now_utc().to_offset(settings.timezone.0); let month = now.month(); @@ -318,7 +318,7 @@ pub(crate) async fn run( ); println!("Your Top Commands:"); - crate::atuin_history::stats::pretty_print(stats.clone(), 1, theme); + crate::atuin_history::stats::pretty_print(stats.clone(), 1); println!(); print_fun_facts(&wrapped_stats, &stats, year); diff --git a/crates/turtle/src/command/mod.rs b/crates/turtle/src/command/mod.rs index 308e1970..78de2d03 100644 --- a/crates/turtle/src/command/mod.rs +++ b/crates/turtle/src/command/mod.rs @@ -4,23 +4,18 @@ use eyre::Result; #[cfg(not(windows))] use rustix::{fs::Mode, process::umask}; -#[cfg(feature = "client")] mod client; - mod contributors; - mod gen_completions; #[derive(Subcommand)] #[command(infer_subcommands = true)] #[expect(clippy::large_enum_variant)] pub(crate) enum AtuinCmd { - #[cfg(feature = "client")] #[command(flatten)] Client(client::Cmd), /// PTY proxy for atuin - #[cfg(feature = "pty-proxy")] #[command(alias = "hex")] PtyProxy(crate::atuin_pty_proxy::PtyProxy), @@ -44,10 +39,8 @@ impl AtuinCmd { } match self { - #[cfg(feature = "client")] Self::Client(client) => client.run(), - #[cfg(feature = "pty-proxy")] Self::PtyProxy(proxy) => { run_pty_proxy(proxy); Ok(()) @@ -66,16 +59,12 @@ impl AtuinCmd { } } -#[cfg(all(feature = "pty-proxy", unix))] +#[cfg(unix)] fn run_pty_proxy(proxy: crate::atuin_pty_proxy::PtyProxy) { - #[cfg(feature = "daemon")] proxy.run(semantic_command_capture_sink()); - - #[cfg(not(feature = "daemon"))] - proxy.run(None); } -#[cfg(all(feature = "daemon", feature = "pty-proxy", unix))] +#[cfg(unix)] fn semantic_command_capture_sink() -> Option<crate::atuin_pty_proxy::CommandCaptureSink> { use std::sync::mpsc; use std::time::Duration; |
