vt: use explicit options structs

This commit is contained in:
Mitchell Hashimoto
2026-03-14 14:26:20 -07:00
parent 4e494ccd68
commit 09d3ebd80d
2 changed files with 114 additions and 29 deletions

View File

@@ -47,21 +47,58 @@ typedef enum {
GHOSTTY_FORMATTER_FORMAT_HTML,
} GhosttyFormatterFormat;
/**
* Extra screen state to include in styled output.
*
* @ingroup formatter
*/
typedef struct {
/** Emit cursor position using CUP (CSI H). */
bool cursor;
/** Emit current SGR style state based on the cursor's active style_id. */
bool style;
/** Emit current hyperlink state using OSC 8 sequences. */
bool hyperlink;
/** Emit character protection mode using DECSCA. */
bool protection;
/** Emit Kitty keyboard protocol state using CSI > u and CSI = sequences. */
bool kitty_keyboard;
/** Emit character set designations and invocations. */
bool charsets;
} GhosttyFormatterScreenExtra;
/**
* Extra terminal state to include in styled output.
*
* @ingroup formatter
*/
typedef enum {
/** Emit no extra state. */
GHOSTTY_FORMATTER_EXTRA_NONE,
typedef struct {
/** Emit the palette using OSC 4 sequences. */
bool palette;
/** Emit style-relevant state (palette, cursor style, hyperlinks). */
GHOSTTY_FORMATTER_EXTRA_STYLES,
/** Emit terminal modes that differ from their defaults using CSI h/l. */
bool modes;
/** Emit all state to reconstruct terminal as closely as possible. */
GHOSTTY_FORMATTER_EXTRA_ALL,
} GhosttyFormatterExtra;
/** Emit scrolling region state using DECSTBM and DECSLRM sequences. */
bool scrolling_region;
/** Emit tabstop positions by clearing all tabs and setting each one. */
bool tabstops;
/** Emit the present working directory using OSC 7. */
bool pwd;
/** Emit keyboard modes such as ModifyOtherKeys. */
bool keyboard;
/** Screen-level extras. */
GhosttyFormatterScreenExtra screen;
} GhosttyFormatterTerminalExtra;
/**
* Opaque handle to a formatter instance.
@@ -86,7 +123,7 @@ typedef struct {
bool trim;
/** Extra terminal state to include in styled output. */
GhosttyFormatterExtra extra;
GhosttyFormatterTerminalExtra extra;
} GhosttyFormatterTerminalOptions;
/**

View File

@@ -23,11 +23,35 @@ pub const Formatter = ?*FormatterWrapper;
/// C: GhosttyFormatterFormat
pub const Format = formatterpkg.Format;
/// C: GhosttyFormatterExtra
pub const Extra = enum(c_int) {
none = 0,
styles = 1,
all = 2,
/// C: GhosttyFormatterScreenOptions
pub const ScreenOptions = extern struct {
/// C: GhosttyFormatterScreenExtra
pub const Extra = extern struct {
cursor: bool,
style: bool,
hyperlink: bool,
protection: bool,
kitty_keyboard: bool,
charsets: bool,
comptime {
for (std.meta.fieldNames(formatterpkg.ScreenFormatter.Extra)) |name| {
if (!@hasField(Extra, name))
@compileError("ScreenOptions.Extra missing field: " ++ name);
}
}
fn toZig(self: Extra) formatterpkg.ScreenFormatter.Extra {
return .{
.cursor = self.cursor,
.style = self.style,
.hyperlink = self.hyperlink,
.protection = self.protection,
.kitty_keyboard = self.kitty_keyboard,
.charsets = self.charsets,
};
}
};
};
/// C: GhosttyFormatterTerminalOptions
@@ -36,6 +60,36 @@ pub const TerminalOptions = extern struct {
unwrap: bool,
trim: bool,
extra: Extra,
/// C: GhosttyFormatterTerminalExtra
pub const Extra = extern struct {
palette: bool,
modes: bool,
scrolling_region: bool,
tabstops: bool,
pwd: bool,
keyboard: bool,
screen: ScreenOptions.Extra,
comptime {
for (std.meta.fieldNames(formatterpkg.TerminalFormatter.Extra)) |name| {
if (!@hasField(Extra, name))
@compileError("TerminalOptions.Extra missing field: " ++ name);
}
}
fn toZig(self: Extra) formatterpkg.TerminalFormatter.Extra {
return .{
.palette = self.palette,
.modes = self.modes,
.scrolling_region = self.scrolling_region,
.tabstops = self.tabstops,
.pwd = self.pwd,
.keyboard = self.keyboard,
.screen = self.screen.toZig(),
};
}
};
};
pub fn terminal_new(
@@ -74,18 +128,12 @@ fn terminal_new_(
return error.OutOfMemory;
errdefer alloc.destroy(ptr);
const extra: formatterpkg.TerminalFormatter.Extra = switch (opts.extra) {
.none => .none,
.styles => .styles,
.all => .all,
};
var formatter: formatterpkg.TerminalFormatter = .init(t, .{
.emit = opts.emit,
.unwrap = opts.unwrap,
.trim = opts.trim,
});
formatter.extra = extra;
formatter.extra = opts.extra.toZig();
ptr.* = .{
.kind = .{ .terminal = formatter },
@@ -145,7 +193,7 @@ test "terminal_new/free" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
try testing.expect(f != null);
free(f);
@@ -157,7 +205,7 @@ test "terminal_new invalid_value on null terminal" {
&lib_alloc.test_allocator,
&f,
null,
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
try testing.expect(f == null);
}
@@ -182,7 +230,7 @@ test "format plain" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
defer free(f);
@@ -208,7 +256,7 @@ test "format reflects terminal changes" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
defer free(f);
@@ -240,7 +288,7 @@ test "format null returns required size" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
defer free(f);
@@ -272,7 +320,7 @@ test "format buffer too small" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .plain, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
defer free(f);
@@ -305,7 +353,7 @@ test "format vt" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .vt, .unwrap = false, .trim = true, .extra = .styles },
.{ .emit = .vt, .unwrap = false, .trim = true, .extra = .{ .palette = true, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = true, .hyperlink = true, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
defer free(f);
@@ -332,7 +380,7 @@ test "format html" {
&lib_alloc.test_allocator,
&f,
t,
.{ .emit = .html, .unwrap = false, .trim = true, .extra = .none },
.{ .emit = .html, .unwrap = false, .trim = true, .extra = .{ .palette = false, .modes = false, .scrolling_region = false, .tabstops = false, .pwd = false, .keyboard = false, .screen = .{ .cursor = false, .style = false, .hyperlink = false, .protection = false, .kitty_keyboard = false, .charsets = false } } },
));
defer free(f);