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:
1 parent
d1a5e55198
commit
2aa2a12de0
61 files changed
+4210
-298
No files matched your search
@@ -442,6 +442,17 @@ impl Parser {
|
||||
Some(self.buffer.clone())
|
||||
}
|
||||
|
||||
/// Zig `writer.writeByte(0)` capacity check: when the fixed buffer is
|
||||
/// full there is no room for the NUL terminator, so the parser becomes
|
||||
/// invalid and this returns false.
|
||||
pub fn reserve_nul(&mut self) -> bool {
|
||||
if self.writer == Writer::Fixed && self.buffer.len() >= Self::MAX_BUF {
|
||||
self.state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Like [`Parser::data`] but without reserving room for the NUL
|
||||
/// terminator (for parsers that, like Zig's color parsers, read
|
||||
/// `writer.buffered()` directly).
|
||||
|
||||
@@ -11,25 +11,49 @@ fn parse_unsigned<T: std::str::FromStr>(bytes: &[u8]) -> Option<T> {
|
||||
|
||||
/// Parse OSC 9, which could be an iTerm2 notification or a ConEmu extension.
|
||||
pub fn parse(parser: &mut Parser, _terminator_ch: Option<u8>) -> Option<Command> {
|
||||
// Zig prologue: writer must exist. (The NUL-terminator capacity check is
|
||||
// folded into `data()`.)
|
||||
let data = parser.data()?;
|
||||
// Zig prologue: writer must exist. The NUL-terminator reservation is NOT
|
||||
// done here: Zig only appends the NUL (`writer.writeByte(0)`) in the
|
||||
// branches that return text, so full-buffer control commands (sleep,
|
||||
// xterm emulation, progress, ...) still parse.
|
||||
let data = parser.buffered()?;
|
||||
|
||||
// Check first to see if this is a ConEmu OSC
|
||||
// https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
|
||||
if let Some(cmd) = conemu(&data) {
|
||||
if let Some((cmd, needs_nul)) = conemu(&data) {
|
||||
if needs_nul && !parser.reserve_nul() {
|
||||
return None;
|
||||
}
|
||||
return Some(cmd);
|
||||
}
|
||||
|
||||
// If it's not a ConEmu OSC, it's an iTerm2 notification
|
||||
if !parser.reserve_nul() {
|
||||
return None;
|
||||
}
|
||||
Some(Command::ShowDesktopNotification {
|
||||
title: Vec::new(),
|
||||
body: data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns None where the Zig code does `break :conemu`.
|
||||
fn conemu(data: &[u8]) -> Option<Command> {
|
||||
/// Returns None where the Zig code does `break :conemu`. The flag is true
|
||||
/// when the Zig branch appends a NUL terminator (needs buffer room).
|
||||
fn conemu(data: &[u8]) -> Option<(Command, bool)> {
|
||||
let r = conemu_inner(data)?;
|
||||
let needs_nul = matches!(
|
||||
r,
|
||||
Command::ConemuComment(_)
|
||||
| Command::ConemuShowMessageBox(_)
|
||||
| Command::ConemuChangeTabTitle(_)
|
||||
| Command::ConemuGuimacro(_)
|
||||
| Command::ConemuRunProcess(_)
|
||||
| Command::ConemuOutputEnvironmentVariable(_)
|
||||
| Command::ReportPwd { .. }
|
||||
);
|
||||
Some((r, needs_nul))
|
||||
}
|
||||
|
||||
fn conemu_inner(data: &[u8]) -> Option<Command> {
|
||||
if data.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -218,6 +242,39 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
fn run_opt(input: &str) -> Option<Command> {
|
||||
let mut p = Parser::init(false);
|
||||
for ch in input.bytes() {
|
||||
p.next(ch);
|
||||
}
|
||||
p.end(Some(0x1b))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_buffer_control_commands_do_not_need_nul() {
|
||||
let fill = Parser::MAX_BUF;
|
||||
// OSC 9;1 sleep with a buffer that has no room for a NUL: Zig never
|
||||
// appends one for this command, so it still parses (default 100ms).
|
||||
let input = format!("9;1;{}", "x".repeat(fill - 2));
|
||||
assert_eq!(
|
||||
run_opt(&input),
|
||||
Some(Command::ConemuSleep { duration_ms: 100 })
|
||||
);
|
||||
// OSC 9;5 wait for input.
|
||||
let input = format!("9;5{}", "x".repeat(fill - 1));
|
||||
assert_eq!(run_opt(&input), Some(Command::ConemuWaitInput));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_buffer_text_commands_are_invalid() {
|
||||
let fill = Parser::MAX_BUF;
|
||||
// These append a NUL in Zig, which fails on a full buffer.
|
||||
let input = format!("9;6;{}", "a".repeat(fill - 2));
|
||||
assert_eq!(run_opt(&input), None);
|
||||
let input = format!("9;{}", "a".repeat(fill));
|
||||
assert_eq!(run_opt(&input), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn osc_9_show_desktop_notification() {
|
||||
notif("9;Hello world", "Hello world");
|
||||
|
||||
Reference in new issue
Block a user