fix: close remaining gaps found by the completeness audit

Addresses the partial ports listed in
docs/rewrite/notes/review-completeness-codex.md (details in
docs/rewrite/notes/gaps-X2.md):
- Flatpak: the exit watcher no longer consumes the host command; a
  shared FlatpakSignalHandle lets closing a surface still SIGHUP the
  host process group.
- Child environment is byte-preserving end to end (ghostty_os::EnvMap
  of OsStrings) and Snap paths are filtered like filterSnapPaths.
- argv handling no longer panics on non-UTF-8 arguments.
- URI percent-decoding keeps raw bytes; OSC 7 pwd is passed through
  without lossy conversion.
- Keybinding comparison/hashing uses full Unicode case folding from a
  table generated from CaseFolding.txt (Unicode 17.0.0).
- Link regexes set Oniguruma's whole-search retry limit through the new
  minimal ghostty-onig crate (one SAFETY-commented FFI call).
- OSC 9 NUL-capacity check ordering matches Zig.
- Wayland registry binds/clears globals advertised after startup.
- Custom shaders may pass sampler2D to helper functions (naga adapter).
- +list-themes preview renders OSC 8 links, styled underlines and
  follows color-scheme reports.

Also includes the inspector's integration edits in shared files
(renderer state hook, termio VT recording, core/apprt-gtk wiring), X1's
config/input/cli additions for completion generation, and a fix for a
race in the termio backpressure test (it wrote before `stty raw`
applied, so the cooked line discipline dropped bytes past MAX_CANON).

Test Plan:
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
This commit is contained in:
ddidderr committed 2026-10-12 04:39:24 +02:00
1 parent d1a5e55198
commit 2aa2a12de0
61 files changed
+4210 -298

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
[package]
name = "ghostty-onig"
version.workspace = true
edition.workspace = true
license.workspace = true
description = "Minimal safe wrapper for the Oniguruma match-param calls the `onig` crate does not expose"
[dependencies]
onig = "6.5.3"
onig_sys = "69.9.3"
# This crate exists solely to hold one tiny, SAFETY-commented FFI call
# (`onig_set_retry_limit_in_search_of_match_param`), which the `onig` crate
# does not expose. Everything else in the workspace keeps `unsafe_code`
# forbidden.
[lints.rust]
unsafe_code = "deny"
[lints.clippy]
pedantic = { level = "warn", priority = -1 }
todo = "warn"
unwrap_used = "warn"
# Porting a low-level terminal emulator involves a lot of intentional
# numeric casts and long state-machine functions; these pedantic lints are
# noise for this codebase.
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"
cast_sign_loss = "allow"
cast_precision_loss = "allow"
cast_lossless = "allow"
module_name_repetitions = "allow"
too_many_lines = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
similar_names = "allow"
struct_excessive_bools = "allow"
fn_params_excessive_bools = "allow"
match_same_arms = "allow"
unreadable_literal = "allow"
many_single_char_names = "allow"
doc_markdown = "allow"
must_use_candidate = "allow"
return_self_not_must_use = "allow"
used_underscore_binding = "allow"
large_stack_arrays = "allow"
inline_always = "allow"
+61
View File
@@ -0,0 +1,61 @@
//! Oniguruma `MatchParam` extensions missing from the `onig` crate.
//!
//! Ghostty's `StringMap` bounds regex work with
//! `MatchParam.setRetryLimitInSearch` (a *whole-search* retry budget); the
//! `onig` crate only wraps the per-match limit, so the search limit is set
//! here through `onig_sys`.
use onig::MatchParam;
/// Set the retry limit in search (Oniguruma's aggregate backtracking-work
/// budget across the entire search, as opposed to per match attempt).
pub fn set_retry_limit_in_search(param: &mut MatchParam, limit: u32) {
#[allow(unsafe_code)]
// SAFETY: `param.as_raw()` is the live, non-null `OnigMatchParam*` owned
// by `param` (allocated by `onig_new_match_param` in
// `MatchParam::default`) and we hold `&mut` so nothing else uses it for
// the duration of the call; the function only writes an integer field.
unsafe {
onig_sys::onig_set_retry_limit_in_search_of_match_param(
param.as_raw(),
std::os::raw::c_ulong::from(limit),
);
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
use onig::{Regex, SearchOptions};
fn search(re: &Regex, text: &str, limit: Option<u32>) -> Result<Option<usize>, onig::Error> {
let mut param = MatchParam::default();
if let Some(l) = limit {
set_retry_limit_in_search(&mut param, l);
}
re.search_with_param(
text,
0,
text.len(),
SearchOptions::SEARCH_OPTION_NONE,
None,
param,
)
}
#[test]
fn small_limit_aborts_catastrophic_search() {
// Classic exponential backtracking pattern.
let re = Regex::new("(a+)+$").unwrap();
let text = format!("{}b", "a".repeat(40));
// With a tiny whole-search budget the search errors out.
assert!(search(&re, &text, Some(1000)).is_err());
}
#[test]
fn generous_limit_allows_normal_search() {
let re = Regex::new("b+").unwrap();
assert_eq!(search(&re, "aaabbb", Some(100_000)).unwrap(), Some(3));
}
}