mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-18 21:40:29 +00:00
terminal: explicit error sets in Screen and ScreenSet
This commit is contained in:
@@ -781,7 +781,11 @@ pub fn clone(
|
||||
alloc: Allocator,
|
||||
opts: Clone,
|
||||
) !PageList {
|
||||
var it = self.pageIterator(.right_down, opts.top, opts.bot);
|
||||
var it = self.pageIterator(
|
||||
.right_down,
|
||||
opts.top,
|
||||
opts.bot,
|
||||
);
|
||||
|
||||
// First, count our pages so our preheat is exactly what we need.
|
||||
var it_copy = it;
|
||||
@@ -2698,7 +2702,7 @@ fn scrollPrompt(self: *PageList, delta: isize) void {
|
||||
|
||||
/// Clear the screen by scrolling written contents up into the scrollback.
|
||||
/// This will not update the viewport.
|
||||
pub fn scrollClear(self: *PageList) !void {
|
||||
pub fn scrollClear(self: *PageList) Allocator.Error!void {
|
||||
defer self.assertIntegrity();
|
||||
|
||||
// Go through the active area backwards to find the first non-empty
|
||||
@@ -4010,7 +4014,10 @@ pub fn getCell(self: *const PageList, pt: point.Point) ?Cell {
|
||||
/// 1 | etc.| | 4
|
||||
/// +-----+ :
|
||||
/// +--------+
|
||||
pub fn diagram(self: *const PageList, writer: *std.Io.Writer) !void {
|
||||
pub fn diagram(
|
||||
self: *const PageList,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
const active_pin = self.getTopLeft(.active);
|
||||
|
||||
var active = false;
|
||||
@@ -4647,7 +4654,7 @@ pub fn totalPages(self: *const PageList) usize {
|
||||
|
||||
/// Grow the number of rows available in the page list by n.
|
||||
/// This is only used for testing so it isn't optimized in any way.
|
||||
fn growRows(self: *PageList, n: usize) !void {
|
||||
fn growRows(self: *PageList, n: usize) Allocator.Error!void {
|
||||
for (0..n) |_| _ = try self.grow();
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ pub const Options = struct {
|
||||
pub fn init(
|
||||
alloc: Allocator,
|
||||
opts: Options,
|
||||
) !Screen {
|
||||
) Allocator.Error!Screen {
|
||||
// Initialize our backing pages.
|
||||
var pages = try PageList.init(
|
||||
alloc,
|
||||
@@ -2324,7 +2324,7 @@ pub fn cursorSetHyperlink(self: *Screen) PageList.IncreaseCapacityError!void {
|
||||
}
|
||||
|
||||
/// Set the selection to the given selection. If this is a tracked selection
|
||||
/// then the screen will take overnship of the selection. If this is untracked
|
||||
/// then the screen will take ownership of the selection. If this is untracked
|
||||
/// then the screen will convert it to tracked internally. This will automatically
|
||||
/// untrack the prior selection (if any).
|
||||
///
|
||||
@@ -2333,7 +2333,7 @@ pub fn cursorSetHyperlink(self: *Screen) PageList.IncreaseCapacityError!void {
|
||||
/// This is always recommended over setting `selection` directly. Beyond
|
||||
/// managing memory for you, it also performs safety checks that the selection
|
||||
/// is always tracked.
|
||||
pub fn select(self: *Screen, sel_: ?Selection) !void {
|
||||
pub fn select(self: *Screen, sel_: ?Selection) Allocator.Error!void {
|
||||
const sel = sel_ orelse {
|
||||
self.clearSelection();
|
||||
return;
|
||||
|
||||
@@ -32,7 +32,7 @@ all: std.EnumMap(Key, *Screen),
|
||||
pub fn init(
|
||||
alloc: Allocator,
|
||||
opts: Screen.Options,
|
||||
) !ScreenSet {
|
||||
) Allocator.Error!ScreenSet {
|
||||
// We need to initialize our initial primary screen
|
||||
const screen = try alloc.create(Screen);
|
||||
errdefer alloc.destroy(screen);
|
||||
@@ -64,7 +64,7 @@ pub fn getInit(
|
||||
alloc: Allocator,
|
||||
key: Key,
|
||||
opts: Screen.Options,
|
||||
) !*Screen {
|
||||
) Allocator.Error!*Screen {
|
||||
if (self.get(key)) |screen| return screen;
|
||||
const screen = try alloc.create(Screen);
|
||||
errdefer alloc.destroy(screen);
|
||||
|
||||
@@ -3,6 +3,7 @@ const Selection = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = @import("../quirks.zig").inlineAssert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const page = @import("page.zig");
|
||||
const point = @import("point.zig");
|
||||
const PageList = @import("PageList.zig");
|
||||
@@ -126,7 +127,7 @@ pub fn tracked(self: *const Selection) bool {
|
||||
|
||||
/// Convert this selection a tracked selection. It is asserted this is
|
||||
/// an untracked selection. The tracked selection is returned.
|
||||
pub fn track(self: *const Selection, s: *Screen) !Selection {
|
||||
pub fn track(self: *const Selection, s: *Screen) Allocator.Error!Selection {
|
||||
assert(!self.tracked());
|
||||
|
||||
// Track our pins
|
||||
|
||||
@@ -1118,7 +1118,7 @@ pub fn cursorIsAtPrompt(self: *Terminal) bool {
|
||||
|
||||
/// Horizontal tab moves the cursor to the next tabstop, clearing
|
||||
/// the screen to the left the tabstop.
|
||||
pub fn horizontalTab(self: *Terminal) !void {
|
||||
pub fn horizontalTab(self: *Terminal) void {
|
||||
while (self.screens.active.cursor.x < self.scrolling_region.right) {
|
||||
// Move the cursor right
|
||||
self.screens.active.cursorRight(1);
|
||||
@@ -1131,7 +1131,7 @@ pub fn horizontalTab(self: *Terminal) !void {
|
||||
}
|
||||
|
||||
// Same as horizontalTab but moves to the previous tabstop instead of the next.
|
||||
pub fn horizontalTabBack(self: *Terminal) !void {
|
||||
pub fn horizontalTabBack(self: *Terminal) void {
|
||||
// With origin mode enabled, our leftmost limit is the left margin.
|
||||
const left_limit = if (self.modes.get(.origin)) self.scrolling_region.left else 0;
|
||||
|
||||
@@ -4736,17 +4736,17 @@ test "Terminal: horizontal tabs" {
|
||||
|
||||
// HT
|
||||
try t.print('1');
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try testing.expectEqual(@as(usize, 8), t.screens.active.cursor.x);
|
||||
|
||||
// HT
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try testing.expectEqual(@as(usize, 16), t.screens.active.cursor.x);
|
||||
|
||||
// HT at the end
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try testing.expectEqual(@as(usize, 19), t.screens.active.cursor.x);
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try testing.expectEqual(@as(usize, 19), t.screens.active.cursor.x);
|
||||
}
|
||||
|
||||
@@ -4758,7 +4758,7 @@ test "Terminal: horizontal tabs starting on tabstop" {
|
||||
t.setCursorPos(t.screens.active.cursor.y, 9);
|
||||
try t.print('X');
|
||||
t.setCursorPos(t.screens.active.cursor.y, 9);
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try t.print('A');
|
||||
|
||||
{
|
||||
@@ -4777,7 +4777,7 @@ test "Terminal: horizontal tabs with right margin" {
|
||||
t.scrolling_region.right = 5;
|
||||
t.setCursorPos(t.screens.active.cursor.y, 1);
|
||||
try t.print('X');
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try t.print('A');
|
||||
|
||||
{
|
||||
@@ -4796,17 +4796,17 @@ test "Terminal: horizontal tabs back" {
|
||||
t.setCursorPos(t.screens.active.cursor.y, 20);
|
||||
|
||||
// HT
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try testing.expectEqual(@as(usize, 16), t.screens.active.cursor.x);
|
||||
|
||||
// HT
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try testing.expectEqual(@as(usize, 8), t.screens.active.cursor.x);
|
||||
|
||||
// HT
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try testing.expectEqual(@as(usize, 0), t.screens.active.cursor.x);
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try testing.expectEqual(@as(usize, 0), t.screens.active.cursor.x);
|
||||
}
|
||||
|
||||
@@ -4818,7 +4818,7 @@ test "Terminal: horizontal tabs back starting on tabstop" {
|
||||
t.setCursorPos(t.screens.active.cursor.y, 9);
|
||||
try t.print('X');
|
||||
t.setCursorPos(t.screens.active.cursor.y, 9);
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try t.print('A');
|
||||
|
||||
{
|
||||
@@ -4838,7 +4838,7 @@ test "Terminal: horizontal tabs with left margin in origin mode" {
|
||||
t.scrolling_region.right = 5;
|
||||
t.setCursorPos(1, 2);
|
||||
try t.print('X');
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try t.print('A');
|
||||
|
||||
{
|
||||
@@ -4858,7 +4858,7 @@ test "Terminal: horizontal tab back with cursor before left margin" {
|
||||
t.modes.set(.enable_left_and_right_margin, true);
|
||||
t.setLeftAndRightMargin(5, 0);
|
||||
t.restoreCursor();
|
||||
try t.horizontalTabBack();
|
||||
t.horizontalTabBack();
|
||||
try t.print('X');
|
||||
|
||||
{
|
||||
@@ -10593,11 +10593,11 @@ test "Terminal: tabClear single" {
|
||||
var t = try init(alloc, .{ .cols = 30, .rows = 5 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
t.tabClear(.current);
|
||||
try testing.expect(!t.isDirty(.{ .active = .{ .x = 0, .y = 0 } }));
|
||||
t.setCursorPos(1, 1);
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try testing.expectEqual(@as(usize, 16), t.screens.active.cursor.x);
|
||||
}
|
||||
|
||||
@@ -10609,7 +10609,7 @@ test "Terminal: tabClear all" {
|
||||
t.tabClear(.all);
|
||||
try testing.expect(!t.isDirty(.{ .active = .{ .x = 0, .y = 0 } }));
|
||||
t.setCursorPos(1, 1);
|
||||
try t.horizontalTab();
|
||||
t.horizontalTab();
|
||||
try testing.expectEqual(@as(usize, 29), t.screens.active.cursor.x);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ pub const Name = enum(u8) {
|
||||
}
|
||||
|
||||
/// Default colors for tagged values.
|
||||
pub fn default(self: Name) !RGB {
|
||||
pub fn default(self: Name) error{NoDefaultValue}!RGB {
|
||||
return switch (self) {
|
||||
.black => RGB{ .r = 0x1D, .g = 0x1F, .b = 0x21 },
|
||||
.red => RGB{ .r = 0xCC, .g = 0x66, .b = 0x66 },
|
||||
@@ -355,7 +355,7 @@ pub const RGB = packed struct(u24) {
|
||||
/// Parse a color from a floating point intensity value.
|
||||
///
|
||||
/// The value should be between 0.0 and 1.0, inclusive.
|
||||
fn fromIntensity(value: []const u8) !u8 {
|
||||
fn fromIntensity(value: []const u8) error{InvalidFormat}!u8 {
|
||||
const i = std.fmt.parseFloat(f64, value) catch {
|
||||
@branchHint(.cold);
|
||||
return error.InvalidFormat;
|
||||
@@ -372,7 +372,7 @@ pub const RGB = packed struct(u24) {
|
||||
///
|
||||
/// The string can contain 1, 2, 3, or 4 characters and represents the color
|
||||
/// value scaled in 4, 8, 12, or 16 bits, respectively.
|
||||
fn fromHex(value: []const u8) !u8 {
|
||||
fn fromHex(value: []const u8) error{InvalidFormat}!u8 {
|
||||
if (value.len == 0 or value.len > 4) {
|
||||
@branchHint(.cold);
|
||||
return error.InvalidFormat;
|
||||
@@ -414,7 +414,7 @@ pub const RGB = packed struct(u24) {
|
||||
/// where `r`, `g`, and `b` are a single hexadecimal digit.
|
||||
/// These specify a color with 4, 8, 12, and 16 bits of precision
|
||||
/// per color channel.
|
||||
pub fn parse(value: []const u8) !RGB {
|
||||
pub fn parse(value: []const u8) error{InvalidFormat}!RGB {
|
||||
if (value.len == 0) {
|
||||
@branchHint(.cold);
|
||||
return error.InvalidFormat;
|
||||
|
||||
@@ -44,16 +44,23 @@ pub const OSC = struct {
|
||||
return {};
|
||||
}
|
||||
|
||||
fn update(self: *OSC, key: u8, value: []const u8) !void {
|
||||
fn update(self: *OSC, key: u8, value: []const u8) error{
|
||||
UnknownKey,
|
||||
InvalidValue,
|
||||
}!void {
|
||||
// All values are numeric, so we can do a small hack here
|
||||
const v = try std.fmt.parseInt(u4, value, 10);
|
||||
const v = std.fmt.parseInt(
|
||||
u4,
|
||||
value,
|
||||
10,
|
||||
) catch return error.InvalidValue;
|
||||
|
||||
switch (key) {
|
||||
's' => {
|
||||
if (v == 0) return error.InvalidValue;
|
||||
self.scale = std.math.cast(u3, v) orelse return error.Overflow;
|
||||
self.scale = std.math.cast(u3, v) orelse return error.InvalidValue;
|
||||
},
|
||||
'w' => self.width = std.math.cast(u3, v) orelse return error.Overflow,
|
||||
'w' => self.width = std.math.cast(u3, v) orelse return error.InvalidValue,
|
||||
'n' => self.numerator = v,
|
||||
'd' => self.denominator = v,
|
||||
'v' => self.valign = std.enums.fromInt(VAlign, v) orelse return error.InvalidValue,
|
||||
@@ -130,7 +137,7 @@ pub fn parse(parser: *Parser, _: ?u8) ?*Command {
|
||||
cmd.update(k[0], value) catch |err| {
|
||||
switch (err) {
|
||||
error.UnknownKey => log.warn("unknown key: '{c}'", .{k[0]}),
|
||||
else => log.warn("invalid value for key '{c}': {}", .{ k[0], err }),
|
||||
error.InvalidValue => log.warn("invalid value for key '{c}': {}", .{ k[0], err }),
|
||||
}
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -102,8 +102,8 @@ pub const Handler = struct {
|
||||
.delete_lines => self.terminal.deleteLines(value),
|
||||
.scroll_up => try self.terminal.scrollUp(value),
|
||||
.scroll_down => self.terminal.scrollDown(value),
|
||||
.horizontal_tab => try self.horizontalTab(value),
|
||||
.horizontal_tab_back => try self.horizontalTabBack(value),
|
||||
.horizontal_tab => self.horizontalTab(value),
|
||||
.horizontal_tab_back => self.horizontalTabBack(value),
|
||||
.tab_clear_current => self.terminal.tabClear(.current),
|
||||
.tab_clear_all => self.terminal.tabClear(.all),
|
||||
.tab_set => self.terminal.tabSet(),
|
||||
@@ -200,18 +200,18 @@ pub const Handler = struct {
|
||||
}
|
||||
}
|
||||
|
||||
inline fn horizontalTab(self: *Handler, count: u16) !void {
|
||||
inline fn horizontalTab(self: *Handler, count: u16) void {
|
||||
for (0..count) |_| {
|
||||
const x = self.terminal.screens.active.cursor.x;
|
||||
try self.terminal.horizontalTab();
|
||||
self.terminal.horizontalTab();
|
||||
if (x == self.terminal.screens.active.cursor.x) break;
|
||||
}
|
||||
}
|
||||
|
||||
inline fn horizontalTabBack(self: *Handler, count: u16) !void {
|
||||
inline fn horizontalTabBack(self: *Handler, count: u16) void {
|
||||
for (0..count) |_| {
|
||||
const x = self.terminal.screens.active.cursor.x;
|
||||
try self.terminal.horizontalTabBack();
|
||||
self.terminal.horizontalTabBack();
|
||||
if (x == self.terminal.screens.active.cursor.x) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,14 @@ const assert = @import("../quirks.zig").inlineAssert;
|
||||
const RGB = @import("color.zig").RGB;
|
||||
|
||||
/// The map of all available X11 colors.
|
||||
pub const map = colorMap() catch @compileError("failed to parse rgb.txt");
|
||||
pub const map = colorMap();
|
||||
|
||||
pub const ColorMap = std.StaticStringMapWithEql(RGB, std.static_string_map.eqlAsciiIgnoreCase);
|
||||
pub const ColorMap = std.StaticStringMapWithEql(
|
||||
RGB,
|
||||
std.static_string_map.eqlAsciiIgnoreCase,
|
||||
);
|
||||
|
||||
fn colorMap() !ColorMap {
|
||||
fn colorMap() ColorMap {
|
||||
@setEvalBranchQuota(500_000);
|
||||
|
||||
const KV = struct { []const u8, RGB };
|
||||
|
||||
@@ -198,8 +198,8 @@ pub const StreamHandler = struct {
|
||||
.print_repeat => try self.terminal.printRepeat(value),
|
||||
.bell => self.bell(),
|
||||
.backspace => self.terminal.backspace(),
|
||||
.horizontal_tab => try self.horizontalTab(value),
|
||||
.horizontal_tab_back => try self.horizontalTabBack(value),
|
||||
.horizontal_tab => self.horizontalTab(value),
|
||||
.horizontal_tab_back => self.horizontalTabBack(value),
|
||||
.linefeed => {
|
||||
@branchHint(.likely);
|
||||
try self.linefeed();
|
||||
@@ -560,18 +560,18 @@ pub const StreamHandler = struct {
|
||||
self.surfaceMessageWriter(.ring_bell);
|
||||
}
|
||||
|
||||
inline fn horizontalTab(self: *StreamHandler, count: u16) !void {
|
||||
inline fn horizontalTab(self: *StreamHandler, count: u16) void {
|
||||
for (0..count) |_| {
|
||||
const x = self.terminal.screens.active.cursor.x;
|
||||
try self.terminal.horizontalTab();
|
||||
self.terminal.horizontalTab();
|
||||
if (x == self.terminal.screens.active.cursor.x) break;
|
||||
}
|
||||
}
|
||||
|
||||
inline fn horizontalTabBack(self: *StreamHandler, count: u16) !void {
|
||||
inline fn horizontalTabBack(self: *StreamHandler, count: u16) void {
|
||||
for (0..count) |_| {
|
||||
const x = self.terminal.screens.active.cursor.x;
|
||||
try self.terminal.horizontalTabBack();
|
||||
self.terminal.horizontalTabBack();
|
||||
if (x == self.terminal.screens.active.cursor.x) break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user