Ports ZIG/src/config (minus C/WASM APIs) and cli/{args,CommaSplitter,
diagnostics}.zig into ghostty-config:
- all 202 options with exact names, types, defaults and verbatim docs,
declared through one config_struct! macro; Zig's comptime reflection is
replaced by the ConfigField/CliTarget traits
- loading: default XDG files, -e, config-file includes (optional '?',
cycle detection), themes incl. light:/dark: pairs, conditional config,
replay, finalize() derived defaults, clone/changed diffing for reload
- Linux/GTK default keybinds built on ghostty_input::binding::Set
- formatter producing +show-config output
Verified against the installed Ghostty 1.3.1: `+show-config --default`
and `--default --docs` match byte for byte (except gtk-opengl-debug,
which differs between debug/release builds), as do two complex loaded
configs.
Open items (docs/rewrite/notes/ghostty-config.md): url regex needs an
Oniguruma-compatible engine (onig) in the renderer; some font/terminal
types are local copies awaiting integration; KeyRemapSet equality is
order sensitive unlike Zig.
Test Plan:
- cargo test -p ghostty-config (255 tests)
- compare `ghostty +show-config --default --docs` with our formatter
135 lines
3.1 KiB
Rust
135 lines
3.1 KiB
Rust
//! Conditional configuration state (port of `config/conditional.zig`).
|
|
//!
|
|
//! Conditionals in Ghostty configuration are based on a static, typed
|
|
//! state of the world instead of a dynamic key-value set.
|
|
|
|
/// The theme of the underlying OS desktop environment.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
|
|
pub enum Theme {
|
|
#[default]
|
|
Light,
|
|
Dark,
|
|
}
|
|
|
|
impl Theme {
|
|
#[must_use]
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Theme::Light => "light",
|
|
Theme::Dark => "dark",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The target OS (Zig: `std.Target.Os.Tag`); only the Linux build is
|
|
/// supported, but the other tags are kept so matching by name works.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum Os {
|
|
Linux,
|
|
Macos,
|
|
Windows,
|
|
Other,
|
|
}
|
|
|
|
impl Os {
|
|
#[must_use]
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Os::Linux => "linux",
|
|
Os::Macos => "macos",
|
|
Os::Windows => "windows",
|
|
Os::Other => "other",
|
|
}
|
|
}
|
|
|
|
/// The OS of the current build.
|
|
#[must_use]
|
|
pub fn current() -> Os {
|
|
if cfg!(target_os = "linux") {
|
|
Os::Linux
|
|
} else if cfg!(target_os = "macos") {
|
|
Os::Macos
|
|
} else if cfg!(target_os = "windows") {
|
|
Os::Windows
|
|
} else {
|
|
Os::Other
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The static, typed state conditionals are matched against.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct State {
|
|
/// The theme of the underlying OS desktop environment.
|
|
pub theme: Theme,
|
|
/// The target OS of the current build.
|
|
pub os: Os,
|
|
}
|
|
|
|
impl Default for State {
|
|
fn default() -> Self {
|
|
Self {
|
|
theme: Theme::Light,
|
|
os: Os::current(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl State {
|
|
/// Tests the conditional against the state and returns true if it matches.
|
|
#[must_use]
|
|
pub fn match_(&self, cond: &Conditional) -> bool {
|
|
let value = match cond.key {
|
|
Key::Theme => self.theme.as_str(),
|
|
Key::Os => self.os.as_str(),
|
|
};
|
|
match cond.op {
|
|
Op::Eq => value == cond.value,
|
|
Op::Ne => value != cond.value,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An enum of the available conditional configuration keys.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum Key {
|
|
Theme,
|
|
Os,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum Op {
|
|
Eq,
|
|
Ne,
|
|
}
|
|
|
|
/// A single conditional that can be true or false.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct Conditional {
|
|
pub key: Key,
|
|
pub op: Op,
|
|
pub value: String,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[allow(clippy::unwrap_used)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn conditional_enum_match() {
|
|
let state = State {
|
|
theme: Theme::Dark,
|
|
..State::default()
|
|
};
|
|
let c = |op, v: &str| Conditional {
|
|
key: Key::Theme,
|
|
op,
|
|
value: v.to_string(),
|
|
};
|
|
assert!(state.match_(&c(Op::Eq, "dark")));
|
|
assert!(!state.match_(&c(Op::Ne, "dark")));
|
|
assert!(state.match_(&c(Op::Ne, "light")));
|
|
}
|
|
}
|