terminal/tmux: clean up error handling, explicit error sets

pull/9803/head
Mitchell Hashimoto 2025-12-01 12:51:09 -08:00
parent 6e016ea81e
commit 7a9dc77a94
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
1 changed files with 56 additions and 18 deletions

View File

@ -4,6 +4,7 @@
//! documentation. //! documentation.
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = @import("../../quirks.zig").inlineAssert; const assert = @import("../../quirks.zig").inlineAssert;
const oni = @import("oniguruma"); const oni = @import("oniguruma");
@ -56,7 +57,11 @@ pub const Parser = struct {
} }
// Handle a byte of input. // Handle a byte of input.
pub fn put(self: *Parser, byte: u8) !?Notification { //
// If we reach our byte limit this will return OutOfMemory. It only
// does this on the first time we exceed the limit; subsequent calls
// will return null as we drop all input in a broken state.
pub fn put(self: *Parser, byte: u8) Allocator.Error!?Notification {
// If we're in a broken state, just do nothing. // If we're in a broken state, just do nothing.
// //
// We have to do this check here before we check the buffer, because if // We have to do this check here before we check the buffer, because if
@ -89,7 +94,15 @@ pub const Parser = struct {
// complete notification we need to parse. // complete notification we need to parse.
.notification => if (byte == '\n') { .notification => if (byte == '\n') {
// We have a complete notification, parse it. // We have a complete notification, parse it.
return try self.parseNotification(); return self.parseNotification() catch {
// If parsing failed, then we do not mark the state
// as broken because we may be able to continue parsing
// other types of notifications.
//
// In the future we may want to emit a notification
// here about unknown or unsupported notifications.
return null;
};
}, },
// If we're in a block then we accumulate until we see a newline // If we're in a block then we accumulate until we see a newline
@ -122,12 +135,16 @@ pub const Parser = struct {
}, },
} }
try self.buffer.writer.writeByte(byte); self.buffer.writer.writeByte(byte) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
};
return null; return null;
} }
fn parseNotification(self: *Parser) !?Notification { const ParseError = error{RegexError};
fn parseNotification(self: *Parser) ParseError!?Notification {
assert(self.state == .notification); assert(self.state == .notification);
const line = line: { const line = line: {
@ -155,13 +172,16 @@ pub const Parser = struct {
self.buffer.clearRetainingCapacity(); self.buffer.clearRetainingCapacity();
return null; return null;
} else if (std.mem.eql(u8, cmd, "%output")) cmd: { } else if (std.mem.eql(u8, cmd, "%output")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%output %([0-9]+) (.+)$", "^%output %([0-9]+) (.+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {
@ -183,13 +203,16 @@ pub const Parser = struct {
self.state = .idle; self.state = .idle;
return .{ .output = .{ .pane_id = id, .data = data } }; return .{ .output = .{ .pane_id = id, .data = data } };
} else if (std.mem.eql(u8, cmd, "%session-changed")) cmd: { } else if (std.mem.eql(u8, cmd, "%session-changed")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%session-changed \\$([0-9]+) (.+)$", "^%session-changed \\$([0-9]+) (.+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {
@ -220,13 +243,16 @@ pub const Parser = struct {
self.state = .idle; self.state = .idle;
return .{ .sessions_changed = {} }; return .{ .sessions_changed = {} };
} else if (std.mem.eql(u8, cmd, "%window-add")) cmd: { } else if (std.mem.eql(u8, cmd, "%window-add")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%window-add @([0-9]+)$", "^%window-add @([0-9]+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {
@ -247,13 +273,16 @@ pub const Parser = struct {
self.state = .idle; self.state = .idle;
return .{ .window_add = .{ .id = id } }; return .{ .window_add = .{ .id = id } };
} else if (std.mem.eql(u8, cmd, "%window-renamed")) cmd: { } else if (std.mem.eql(u8, cmd, "%window-renamed")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%window-renamed @([0-9]+) (.+)$", "^%window-renamed @([0-9]+) (.+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {
@ -275,13 +304,16 @@ pub const Parser = struct {
self.state = .idle; self.state = .idle;
return .{ .window_renamed = .{ .id = id, .name = name } }; return .{ .window_renamed = .{ .id = id, .name = name } };
} else if (std.mem.eql(u8, cmd, "%window-pane-changed")) cmd: { } else if (std.mem.eql(u8, cmd, "%window-pane-changed")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%window-pane-changed @([0-9]+) %([0-9]+)$", "^%window-pane-changed @([0-9]+) %([0-9]+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {
@ -307,13 +339,16 @@ pub const Parser = struct {
self.state = .idle; self.state = .idle;
return .{ .window_pane_changed = .{ .window_id = window_id, .pane_id = pane_id } }; return .{ .window_pane_changed = .{ .window_id = window_id, .pane_id = pane_id } };
} else if (std.mem.eql(u8, cmd, "%client-detached")) cmd: { } else if (std.mem.eql(u8, cmd, "%client-detached")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%client-detached (.+)$", "^%client-detached (.+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {
@ -330,13 +365,16 @@ pub const Parser = struct {
self.state = .idle; self.state = .idle;
return .{ .client_detached = .{ .client = client } }; return .{ .client_detached = .{ .client = client } };
} else if (std.mem.eql(u8, cmd, "%client-session-changed")) cmd: { } else if (std.mem.eql(u8, cmd, "%client-session-changed")) cmd: {
var re = try oni.Regex.init( var re = oni.Regex.init(
"^%client-session-changed (.+) \\$([0-9]+) (.+)$", "^%client-session-changed (.+) \\$([0-9]+) (.+)$",
.{ .capture_group = true }, .{ .capture_group = true },
oni.Encoding.utf8, oni.Encoding.utf8,
oni.Syntax.default, oni.Syntax.default,
null, null,
); ) catch |err| {
log.warn("regex init failed error={}", .{err});
return error.RegexError;
};
defer re.deinit(); defer re.deinit();
var region = re.search(line, .{}) catch |err| { var region = re.search(line, .{}) catch |err| {