aboutsummaryrefslogtreecommitdiffstats
path: root/crates/client/src/atuin_client
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-07-20 23:00:06 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-07-20 23:00:06 +0200
commit2f671f196e245ac1a791c001286e401a2fab9d3a (patch)
treebe90d8f16d5c8b65c6b77ebe1359d22e9738bbf6 /crates/client/src/atuin_client
parentchore: Commit (diff)
downloadatuin-2f671f196e245ac1a791c001286e401a2fab9d3a.zip
chore: Commit
Diffstat (limited to 'crates/client/src/atuin_client')
-rw-r--r--crates/client/src/atuin_client/mod.rs16
-rw-r--r--crates/client/src/atuin_client/settings/mod.rs393
-rw-r--r--crates/client/src/atuin_client/theme.rs41
3 files changed, 25 insertions, 425 deletions
diff --git a/crates/client/src/atuin_client/mod.rs b/crates/client/src/atuin_client/mod.rs
index fa40d8dd..3f36722f 100644
--- a/crates/client/src/atuin_client/mod.rs
+++ b/crates/client/src/atuin_client/mod.rs
@@ -1,2 +1,16 @@
pub(crate) mod settings;
-// pub(crate) mod theme;
+
+pub async fn current_context(session: String) -> eyre::Result<Context> {
+ let hostname = get_host_user();
+ let cwd = utils::get_current_dir();
+ let host_id = Settings::host_id().await?;
+ let git_root = utils::in_git_repo(cwd.as_str());
+
+ Ok(Context {
+ session,
+ hostname,
+ cwd,
+ git_root,
+ host_id: host_id.0.as_simple().to_string(),
+ })
+}
diff --git a/crates/client/src/atuin_client/settings/mod.rs b/crates/client/src/atuin_client/settings/mod.rs
index bcec25db..5a3a1525 100644
--- a/crates/client/src/atuin_client/settings/mod.rs
+++ b/crates/client/src/atuin_client/settings/mod.rs
@@ -15,35 +15,6 @@ use turtle_common::utils;
static DATA_DIR: OnceLock<PathBuf> = OnceLock::new();
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-pub(crate) enum ExitMode {
- #[serde(rename = "return-original")]
- ReturnOriginal,
-
- #[serde(rename = "return-query")]
- ReturnQuery,
-}
-
-// FIXME: Can use upstream Dialect enum if https://github.com/stevedonovan/chrono-english/pull/16 is merged
-// FIXME: Above PR was merged, but dependency was changed to interim (fork of chrono-english) in the ... interim
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-pub(crate) enum Dialect {
- #[serde(rename = "us")]
- Us,
-
- #[serde(rename = "uk")]
- Uk,
-}
-
-impl From<Dialect> for interim::Dialect {
- fn from(d: Dialect) -> Self {
- match d {
- Dialect::Uk => Self::Uk,
- Dialect::Us => Self::Us,
- }
- }
-}
-
/// Type wrapper around `time::UtcOffset` to support a wider variety of timezone formats.
///
/// Note that the parsing of this struct needs to be done before starting any
@@ -94,79 +65,19 @@ impl FromStr for Timezone {
}
}
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-pub(crate) enum Style {
- #[serde(rename = "auto")]
- Auto,
-
- #[serde(rename = "full")]
- Full,
-
- #[serde(rename = "compact")]
- Compact,
-}
-
-#[derive(Clone, Debug, Deserialize, Copy, Serialize)]
-pub(crate) enum WordJumpMode {
- #[serde(rename = "emacs")]
- Emacs,
-
- #[serde(rename = "subl")]
- Subl,
-}
-
-#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)]
-pub(crate) enum KeymapMode {
- #[serde(rename = "emacs")]
- Emacs,
-
- #[serde(rename = "vim-normal")]
- VimNormal,
-
- #[serde(rename = "vim-insert")]
- VimInsert,
-
- #[serde(rename = "auto")]
- Auto,
-}
-
-// We want to translate the config to crossterm::cursor::SetCursorStyle, but
-// the original type does not implement trait serde::Deserialize unfortunately.
-// It seems impossible to implement Deserialize for external types when it is
-// used in HashMap (https://stackoverflow.com/questions/67142663). We instead
-// define an adapter type.
-#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)]
-pub(crate) enum CursorStyle {
- #[serde(rename = "default")]
- DefaultUserShape,
-
- #[serde(rename = "blink-block")]
- BlinkingBlock,
-
- #[serde(rename = "steady-block")]
- SteadyBlock,
-
- #[serde(rename = "blink-underline")]
- BlinkingUnderScore,
-
- #[serde(rename = "steady-underline")]
- SteadyUnderScore,
-
- #[serde(rename = "blink-bar")]
- BlinkingBar,
-
- #[serde(rename = "steady-bar")]
- SteadyBar,
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct Stats {
+ /// sudo, etc. commands we want to strip off
#[serde(default = "Stats::common_prefix_default")]
- pub(crate) common_prefix: Vec<String>, // sudo, etc. commands we want to strip off
+ pub(crate) common_prefix: Vec<String>,
+
+ /// kubectl, commands we should consider subcommands for
#[serde(default = "Stats::common_subcommands_default")]
- pub(crate) common_subcommands: Vec<String>, // kubectl, commands we should consider subcommands for
+ pub(crate) common_subcommands: Vec<String>,
+
+ /// cd, ls, etc. commands we want to completely hide from stats
#[serde(default = "Stats::ignored_commands_default")]
- pub(crate) ignored_commands: Vec<String>, // cd, ls, etc. commands we want to completely hide from stats
+ pub(crate) ignored_commands: Vec<String>,
}
impl Stats {
@@ -218,86 +129,7 @@ impl Default for Stats {
}
}
-#[derive(Clone, Debug, Deserialize, Default, Serialize)]
-#[expect(clippy::struct_excessive_bools)]
-pub(crate) struct Keys {
- pub(crate) scroll_exits: bool,
- pub(crate) exit_past_line_start: bool,
- pub(crate) accept_past_line_end: bool,
- pub(crate) accept_past_line_start: bool,
- pub(crate) accept_with_backspace: bool,
- pub(crate) prefix: String,
-}
-
-impl Keys {
- /// The standard default values for all `[keys]` options.
- /// These match the config defaults set in `builder_with_data_dir()`.
- pub(crate) fn standard_defaults() -> Self {
- Self {
- scroll_exits: true,
- exit_past_line_start: true,
- accept_past_line_end: true,
- accept_past_line_start: false,
- accept_with_backspace: false,
- prefix: "a".to_string(),
- }
- }
-}
-
-/// A single rule within a conditional keybinding config.
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub(crate) struct KeyRuleConfig {
- /// Optional condition expression (e.g. "cursor-at-start", "input-empty && no-results").
- /// If absent, the rule always matches.
- #[serde(default)]
- pub(crate) when: Option<String>,
- /// The action to perform (e.g. "exit", "cursor-left", "accept").
- pub(crate) action: String,
-}
-
-/// A keybinding config value: either a simple action string or an ordered list of conditional rules.
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(untagged)]
-pub(crate) enum KeyBindingConfig {
- /// Simple unconditional binding: `"ctrl-c" = "return-original"`
- Simple(String),
- /// Conditional binding: `"left" = [{ when = "cursor-at-start", action = "exit" }, { action = "cursor-left" }]`
- Rules(Vec<KeyRuleConfig>),
-}
-
-/// User-facing keymap configuration. Each mode maps key strings to bindings.
-/// Keys present here override the defaults for that key; unmentioned keys keep defaults.
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
-pub(crate) struct KeymapConfig {
- #[serde(default)]
- pub(crate) emacs: HashMap<String, KeyBindingConfig>,
- #[serde(default, rename = "vim-normal")]
- pub(crate) vim_normal: HashMap<String, KeyBindingConfig>,
- #[serde(default, rename = "vim-insert")]
- pub(crate) vim_insert: HashMap<String, KeyBindingConfig>,
- #[serde(default)]
- pub(crate) inspector: HashMap<String, KeyBindingConfig>,
- #[serde(default)]
- pub(crate) prefix: HashMap<String, KeyBindingConfig>,
-}
-
-impl KeymapConfig {
- /// Returns true if no keybinding overrides are configured in any mode.
- pub(crate) fn is_empty(&self) -> bool {
- self.emacs.is_empty()
- && self.vim_normal.is_empty()
- && self.vim_insert.is_empty()
- && self.inspector.is_empty()
- && self.prefix.is_empty()
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub(crate) struct Preview {
- pub(crate) strategy: PreviewStrategy,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct Daemon {
/// The path to the unix socket used by the daemon
pub(crate) socket_path: String,
@@ -306,104 +138,6 @@ pub(crate) struct Daemon {
pub(crate) systemd_socket: bool,
}
-/// Log level for file logging. Maps to tracing's [`LevelFilter`].
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
-#[serde(rename_all = "lowercase")]
-pub(crate) enum LogLevel {
- Trace,
- Debug,
- #[default]
- Info,
- Warn,
- Error,
-}
-
-impl LogLevel {
- /// Convert to a tracing directive string for use with [`EnvFilter`].
- pub(crate) fn as_directive(self) -> &'static str {
- match self {
- Self::Trace => "trace",
- Self::Debug => "debug",
- Self::Info => "info",
- Self::Warn => "warn",
- Self::Error => "error",
- }
- }
-}
-
-/// Configuration for a specific log type (search or daemon).
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub(crate) struct LogConfig {
- /// Log file name (relative to dir) or absolute path.
- pub(crate) file: String,
-
- /// Override global enabled setting for this log type.
- pub(crate) enabled: Option<bool>,
-
- /// Override global level setting for this log type.
- pub(crate) level: Option<LogLevel>,
-
- /// Override global retention days setting for this log type.
- pub(crate) retention: Option<u64>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub(crate) struct Logs {
- /// Enable file logging globally. Defaults to true.
- #[serde(default = "Logs::default_enabled")]
- pub(crate) enabled: bool,
-
- /// Directory for log files. Defaults to ~/.atuin/logs
- pub(crate) dir: String,
-
- /// Default log level for file logging. Defaults to "info".
- /// Note: [`ATUIN_LOG`] environment variable overrides this.
- #[serde(default)]
- pub(crate) level: LogLevel,
-
- /// Default retention days for log files. Defaults to 4.
- #[serde(default = "Logs::default_retention")]
- pub(crate) retention: u64,
-}
-
-impl Default for Preview {
- fn default() -> Self {
- Self {
- strategy: PreviewStrategy::Auto,
- }
- }
-}
-
-impl Default for Daemon {
- fn default() -> Self {
- Self {
- socket_path: String::new(),
- systemd_socket: false,
- }
- }
-}
-
-impl Default for Logs {
- fn default() -> Self {
- Self {
- enabled: true,
- dir: String::new(),
- level: LogLevel::default(),
- retention: Self::default_retention(),
- }
- }
-}
-
-impl Logs {
- fn default_enabled() -> bool {
- true
- }
-
- fn default_retention() -> u64 {
- 4
- }
-}
-
// The preview height strategy also takes max_preview_height into account.
#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)]
pub(crate) enum PreviewStrategy {
@@ -421,33 +155,11 @@ pub(crate) enum PreviewStrategy {
}
#[derive(Clone, Debug, Deserialize, Serialize)]
-#[expect(clippy::struct_excessive_bools)]
pub(crate) struct Settings {
- pub(crate) data_dir: Option<String>,
- pub(crate) dialect: Dialect,
+ data_dir: Option<String>,
pub(crate) timezone: Timezone,
- pub(crate) style: Style,
- pub(crate) shell_up_key_binding: bool,
- pub(crate) invert: bool,
- pub(crate) show_preview: bool,
- pub(crate) max_preview_height: u16,
- pub(crate) show_help: bool,
- pub(crate) show_tabs: bool,
- pub(crate) show_numeric_shortcuts: bool,
- pub(crate) auto_hide_height: u16,
- pub(crate) exit_mode: ExitMode,
- pub(crate) keymap_mode: KeymapMode,
- pub(crate) keymap_mode_shell: KeymapMode,
- pub(crate) keymap_cursor: HashMap<String, CursorStyle>,
- pub(crate) word_jump_mode: WordJumpMode,
- pub(crate) word_chars: String,
- pub(crate) scroll_context_lines: usize,
- pub(crate) history_format: String,
pub(crate) strip_trailing_whitespace: bool,
- pub(crate) prefers_reduced_motion: bool,
- pub(crate) store_failed: bool,
- pub(crate) no_mouse: bool,
#[serde(with = "serde_regex", default = "RegexSet::empty", skip_serializing)]
pub(crate) history_filter: RegexSet,
@@ -456,37 +168,16 @@ pub(crate) struct Settings {
pub(crate) cwd_filter: RegexSet,
pub(crate) secrets_filter: bool,
- pub(crate) workspaces: bool,
- pub(crate) ctrl_n_shortcuts: bool,
-
- pub(crate) network_connect_timeout: u64,
- pub(crate) network_timeout: u64,
- pub(crate) local_timeout: f64,
- pub(crate) enter_accept: bool,
- pub(crate) smart_sort: bool,
- pub(crate) command_chaining: bool,
#[serde(default)]
pub(crate) stats: Stats,
#[serde(default)]
- pub(crate) keys: Keys,
-
- #[serde(default)]
- pub(crate) keymap: KeymapConfig,
-
- #[serde(default)]
- pub(crate) preview: Preview,
-
- #[serde(default)]
pub(crate) daemon: Daemon,
-
- #[serde(default)]
- pub(crate) logs: Logs,
}
impl Settings {
- pub(crate) fn builder() -> Result<ConfigBuilder<DefaultState>> {
+ fn builder() -> Result<ConfigBuilder<DefaultState>> {
Self::builder_with_data_dir(&utils::data_dir())
}
@@ -910,68 +601,4 @@ mod tests {
Ok(())
}
-
- #[test]
- fn keymap_config_deserializes_simple_binding() {
- let json = r#"{"emacs": {"ctrl-c": "exit"}}"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- assert_eq!(config.emacs.len(), 1);
- match &config.emacs["ctrl-c"] {
- super::KeyBindingConfig::Simple(s) => assert_eq!(s, "exit"),
- _ => panic!("expected Simple variant"),
- }
- }
-
- #[test]
- fn keymap_config_deserializes_conditional_binding() {
- let json = r#"{
- "emacs": {
- "left": [
- {"when": "cursor-at-start", "action": "exit"},
- {"action": "cursor-left"}
- ]
- }
- }"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- match &config.emacs["left"] {
- super::KeyBindingConfig::Rules(rules) => {
- assert_eq!(rules.len(), 2);
- assert_eq!(rules[0].when.as_deref(), Some("cursor-at-start"));
- assert_eq!(rules[0].action, "exit");
- assert!(rules[1].when.is_none());
- assert_eq!(rules[1].action, "cursor-left");
- }
- _ => panic!("expected Rules variant"),
- }
- }
-
- #[test]
- fn keymap_config_deserializes_vim_normal() {
- let json = r#"{"vim-normal": {"j": "select-next", "k": "select-previous"}}"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- assert_eq!(config.vim_normal.len(), 2);
- assert!(config.emacs.is_empty());
- }
-
- #[test]
- fn keymap_config_is_empty_when_default() {
- let config = super::KeymapConfig::default();
- assert!(config.is_empty());
- }
-
- #[test]
- fn keymap_config_mixed_modes() {
- let json = r#"{
- "emacs": {"ctrl-c": "exit"},
- "vim-normal": {"q": "exit"},
- "inspector": {"d": "delete"}
- }"#;
- let config: super::KeymapConfig = serde_json::from_str(json).unwrap();
- assert!(!config.is_empty());
- assert_eq!(config.emacs.len(), 1);
- assert_eq!(config.vim_normal.len(), 1);
- assert_eq!(config.inspector.len(), 1);
- assert!(config.vim_insert.is_empty());
- assert!(config.prefix.is_empty());
- }
}
diff --git a/crates/client/src/atuin_client/theme.rs b/crates/client/src/atuin_client/theme.rs
deleted file mode 100644
index ec0538e9..00000000
--- a/crates/client/src/atuin_client/theme.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use crossterm::style::{Attribute, Attributes, Color, ContentStyle};
-pub(crate) fn style_base() -> ContentStyle {
- ContentStyle::default()
-}
-pub(crate) fn style_annotation() -> ContentStyle {
- ContentStyle {
- foreground_color: Some(Color::DarkGrey),
- ..ContentStyle::default()
- }
-}
-pub(crate) fn style_important() -> ContentStyle {
- ContentStyle {
- foreground_color: Some(Color::White),
- attributes: Attributes::from(Attribute::Bold),
- ..ContentStyle::default()
- }
-}
-pub(crate) fn style_guidance() -> ContentStyle {
- ContentStyle {
- foreground_color: Some(Color::DarkBlue),
- ..ContentStyle::default()
- }
-}
-pub(crate) fn style_alerterror() -> ContentStyle {
- ContentStyle {
- foreground_color: Some(Color::DarkRed),
- ..ContentStyle::default()
- }
-}
-pub(crate) fn style_alertinfo() -> ContentStyle {
- ContentStyle {
- foreground_color: Some(Color::DarkGreen),
- ..ContentStyle::default()
- }
-}
-pub(crate) fn style_alertwarn() -> ContentStyle {
- ContentStyle {
- foreground_color: Some(Color::DarkYellow),
- ..ContentStyle::default()
- }
-}