feat(config): port the configuration system and CLI argument parser
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
This commit is contained in:
1 parent
47b479dcfb
commit
67367dcaf5
34 files changed
+14894
-1
No files matched your search
@@ -0,0 +1,103 @@
|
||||
//! `FileFormatter` (part of `config/formatter.zig`): outputs the config in a
|
||||
//! file-like format, identical to Ghostty's `+show-config`.
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::key::Key;
|
||||
use crate::macros::{ConfigStruct, help_string};
|
||||
use std::fmt;
|
||||
|
||||
/// FileFormatter is a formatter implementation that outputs the
|
||||
/// config in a file-like format. This uses more generous whitespace,
|
||||
/// can include comments, etc.
|
||||
pub struct FileFormatter<'a> {
|
||||
pub config: &'a Config,
|
||||
/// Include comments for documentation of each key
|
||||
pub docs: bool,
|
||||
/// Only include changed values from the default.
|
||||
pub changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> FileFormatter<'a> {
|
||||
#[must_use]
|
||||
pub fn new(config: &'a Config) -> Self {
|
||||
Self {
|
||||
config,
|
||||
docs: false,
|
||||
changed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FileFormatter<'_> {
|
||||
fn fmt(&self, writer: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.format(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileFormatter<'_> {
|
||||
pub fn format(&self, writer: &mut dyn fmt::Write) -> fmt::Result {
|
||||
// If we're change-tracking then we need the default config to
|
||||
// compare against.
|
||||
let default = if self.changed {
|
||||
Some(Config::default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for field in Config::FIELDS {
|
||||
let do_format = match &default {
|
||||
Some(d) => d.changed(self.config, Key(field.name)),
|
||||
None => true,
|
||||
};
|
||||
|
||||
if do_format {
|
||||
let do_docs = self.docs && !field.doc.is_empty();
|
||||
if do_docs {
|
||||
let help = help_string(field.doc);
|
||||
for line in help.split('\n') {
|
||||
writeln!(writer, "# {line}")?;
|
||||
}
|
||||
}
|
||||
|
||||
(field.format)(self.config, field.name, writer)?;
|
||||
|
||||
if do_docs {
|
||||
writeln!(writer)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(
|
||||
clippy::unwrap_used,
|
||||
clippy::field_reassign_with_default,
|
||||
clippy::assert_is_empty
|
||||
)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn format_default_config() {
|
||||
let cfg = Config::default();
|
||||
let mut buf = String::new();
|
||||
FileFormatter::new(&cfg).format(&mut buf).unwrap();
|
||||
assert!(!buf.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_default_config_changed() {
|
||||
let mut cfg = Config::default();
|
||||
cfg.font_size = 42.0;
|
||||
let mut buf = String::new();
|
||||
let f = FileFormatter {
|
||||
config: &cfg,
|
||||
docs: false,
|
||||
changed: true,
|
||||
};
|
||||
f.format(&mut buf).unwrap();
|
||||
assert!(buf.contains("font-size = 42\n"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user