fix(config): match Zig's numeric grammar, NUL handling and diagnostics
Resolves the 6 differential gap groups in docs/rewrite/notes/review- rv9-codex.md (details in final-Z11.md): - ghostty_os::zig_fmt ports std.fmt.parseInt/parseUnsigned/parseFloat (underscores, 0x/0o/0b prefixes, hex floats, inf/nan) with the Zig std tests and replaces every int/float parse in config, keybind parameters, ssh-cache and desktop detection. - Interior NUL no longer panics in font names and ends C strings like Zig's backing bytes (fontconfig, C config getters, title actions, clipboard callback, relative paths, config-file, shaders, background image, GTK CSS). - GTK new-window arguments are trimmed of ASCII whitespace only. - Command tokenization uses a port of std.process.ArgIteratorGeneral (incl. CR/LF separators) with its std tests. - UTF-8 validation happens where Zig's field parsers do it, so error kinds match (metric modifiers, quick-terminal size, link, ...). A scripted differential run of 202 fields x ~220 hostile/valid values against /usr/bin/ghostty (+validate-config and +show-config: exit code, stdout, stderr) now has zero mismatches besides build-specific defaults (theme resources path, auto-update-channel, gtk-opengl-debug); it also surfaced and fixed background-blur failure state, NaN formatting and NaN/-0 finalize clamps. Regression tests: ghostty-config/tests/ zig_parity.rs. Test Plan: - cargo test --workspace --no-fail-fast (3456 passed, exit 0) - just check-macos (both), just check-windows, just check-wasm
This commit is contained in:
1 parent
7480f99d91
commit
79a7fd4f76
26 files changed
+1835
-228
No files matched your search
@@ -4224,7 +4224,7 @@ impl Config {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file = match path.to_path().and_then(std::fs::File::open) {
|
||||
let file = match path.to_syscall_path().and_then(std::fs::File::open) {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
if err.kind() != std::io::ErrorKind::NotFound || !optional {
|
||||
@@ -4564,16 +4564,19 @@ impl Config {
|
||||
}
|
||||
|
||||
// Clamp our mouse scroll multiplier
|
||||
// (`@min(10_000, @max(0.01, v))`: NaN becomes the minimum.)
|
||||
let zmin = cli_args::zig_min;
|
||||
let zmax = cli_args::zig_max;
|
||||
self.mouse_scroll_multiplier.precision =
|
||||
self.mouse_scroll_multiplier.precision.clamp(0.01, 10_000.0);
|
||||
zmin(10_000.0, zmax(0.01, self.mouse_scroll_multiplier.precision));
|
||||
self.mouse_scroll_multiplier.discrete =
|
||||
self.mouse_scroll_multiplier.discrete.clamp(0.01, 10_000.0);
|
||||
zmin(10_000.0, zmax(0.01, self.mouse_scroll_multiplier.discrete));
|
||||
|
||||
// Clamp our split opacity
|
||||
self.unfocused_split_opacity = self.unfocused_split_opacity.clamp(0.15, 1.0);
|
||||
self.unfocused_split_opacity = zmin(1.0, zmax(0.15, self.unfocused_split_opacity));
|
||||
|
||||
// Clamp our contrast
|
||||
self.minimum_contrast = self.minimum_contrast.clamp(1.0, 21.0);
|
||||
self.minimum_contrast = zmin(21.0, zmax(1.0, self.minimum_contrast));
|
||||
|
||||
// Minimum window size
|
||||
if self.window_width > 0 {
|
||||
@@ -4606,7 +4609,7 @@ impl Config {
|
||||
self.auto_update_channel = Some(ReleaseChannel::Stable);
|
||||
}
|
||||
|
||||
self.faint_opacity = self.faint_opacity.clamp(0.0, 1.0);
|
||||
self.faint_opacity = cli_args::zig_clamp(self.faint_opacity, 0.0, 1.0);
|
||||
|
||||
// Finalize key remapping set for efficient lookups
|
||||
self.key_remap.finalize();
|
||||
@@ -4753,14 +4756,37 @@ impl Iterator for ChangeIterator<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of an IO error like Zig's error names (best effort), used in
|
||||
/// user-visible diagnostics.
|
||||
/// Name of an IO error like Zig's `{}` formatting of an error value
|
||||
/// (`error.FileNotFound`), used in user-visible diagnostics.
|
||||
fn zig_io_error_name(err: &std::io::Error) -> String {
|
||||
use std::io::ErrorKind;
|
||||
#[cfg(unix)]
|
||||
if let Some(errno) = err.raw_os_error() {
|
||||
let name = match errno {
|
||||
2 => Some("FileNotFound"),
|
||||
13 => Some("AccessDenied"),
|
||||
1 => Some("PermissionDenied"),
|
||||
21 => Some("IsDir"),
|
||||
20 => Some("NotDir"),
|
||||
40 => Some("SymLinkLoop"),
|
||||
36 => Some("NameTooLong"),
|
||||
24 => Some("ProcessFdQuotaExceeded"),
|
||||
23 => Some("SystemFdQuotaExceeded"),
|
||||
12 => Some("SystemResources"),
|
||||
19 => Some("NoDevice"),
|
||||
16 => Some("DeviceBusy"),
|
||||
26 => Some("FileBusy"),
|
||||
30 => Some("ReadOnlyFileSystem"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(name) = name {
|
||||
return format!("error.{name}");
|
||||
}
|
||||
}
|
||||
match err.kind() {
|
||||
ErrorKind::NotFound => "FileNotFound".to_string(),
|
||||
ErrorKind::PermissionDenied => "AccessDenied".to_string(),
|
||||
_ => err.to_string(),
|
||||
ErrorKind::NotFound => "error.FileNotFound".to_string(),
|
||||
ErrorKind::PermissionDenied => "error.AccessDenied".to_string(),
|
||||
_ => "error.Unexpected".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user