mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-06-03 02:18:12 +00:00
Zig 0.15: zig build test
This commit is contained in:
261
src/cli/args.zig
261
src/cli/args.zig
@@ -162,10 +162,11 @@ pub fn parse(
|
||||
error.InvalidField => "unknown field",
|
||||
error.ValueRequired => formatValueRequired(T, arena_alloc, key) catch "value required",
|
||||
error.InvalidValue => formatInvalidValue(T, arena_alloc, key, value) catch "invalid value",
|
||||
else => try std.fmt.allocPrintZ(
|
||||
else => try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"unknown error {}",
|
||||
.{err},
|
||||
0,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -235,14 +236,16 @@ fn formatValueRequired(
|
||||
comptime T: type,
|
||||
arena_alloc: std.mem.Allocator,
|
||||
key: []const u8,
|
||||
) std.mem.Allocator.Error![:0]const u8 {
|
||||
var buf = std.ArrayList(u8).init(arena_alloc);
|
||||
errdefer buf.deinit();
|
||||
const writer = buf.writer();
|
||||
) std.Io.Writer.Error![:0]const u8 {
|
||||
var stream: std.Io.Writer.Allocating = .init(arena_alloc);
|
||||
const writer = &stream.writer;
|
||||
|
||||
try writer.print("value required", .{});
|
||||
try formatValues(T, key, writer);
|
||||
try writer.writeByte(0);
|
||||
return buf.items[0 .. buf.items.len - 1 :0];
|
||||
|
||||
const written = stream.written();
|
||||
return written[0 .. written.len - 1 :0];
|
||||
}
|
||||
|
||||
fn formatInvalidValue(
|
||||
@@ -250,17 +253,23 @@ fn formatInvalidValue(
|
||||
arena_alloc: std.mem.Allocator,
|
||||
key: []const u8,
|
||||
value: ?[]const u8,
|
||||
) std.mem.Allocator.Error![:0]const u8 {
|
||||
var buf = std.ArrayList(u8).init(arena_alloc);
|
||||
errdefer buf.deinit();
|
||||
const writer = buf.writer();
|
||||
) std.Io.Writer.Error![:0]const u8 {
|
||||
var stream: std.Io.Writer.Allocating = .init(arena_alloc);
|
||||
const writer = &stream.writer;
|
||||
|
||||
try writer.print("invalid value \"{?s}\"", .{value});
|
||||
try formatValues(T, key, writer);
|
||||
try writer.writeByte(0);
|
||||
return buf.items[0 .. buf.items.len - 1 :0];
|
||||
|
||||
const written = stream.written();
|
||||
return written[0 .. written.len - 1 :0];
|
||||
}
|
||||
|
||||
fn formatValues(comptime T: type, key: []const u8, writer: anytype) std.mem.Allocator.Error!void {
|
||||
fn formatValues(
|
||||
comptime T: type,
|
||||
key: []const u8,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
@setEvalBranchQuota(2000);
|
||||
const typeinfo = @typeInfo(T);
|
||||
inline for (typeinfo.@"struct".fields) |f| {
|
||||
@@ -542,8 +551,8 @@ pub fn parseAutoStruct(
|
||||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
|
||||
// used if we need to decode a double-quoted string.
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer buf.deinit(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
const value = value: {
|
||||
const value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
@@ -554,10 +563,9 @@ pub fn parseAutoStruct(
|
||||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Decode a double-quoted string as a Zig string literal.
|
||||
const writer = buf.writer(alloc);
|
||||
const parsed = try std.zig.string_literal.parseWrite(writer, value);
|
||||
const parsed = try std.zig.string_literal.parseWrite(&buf.writer, value);
|
||||
if (parsed == .failure) return error.InvalidValue;
|
||||
break :value buf.items;
|
||||
break :value buf.written();
|
||||
}
|
||||
|
||||
break :value value;
|
||||
@@ -795,15 +803,13 @@ test "parse: diagnostic location" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var r: std.Io.Reader = .fixed(
|
||||
\\a=42
|
||||
\\what
|
||||
\\b=two
|
||||
);
|
||||
const r = fbs.reader();
|
||||
|
||||
const Iter = LineIterator(@TypeOf(r));
|
||||
var iter: Iter = .{ .r = r, .filepath = "test" };
|
||||
var iter: LineIterator = .{ .r = &r, .filepath = "test" };
|
||||
try parse(@TypeOf(data), testing.allocator, &data, &iter);
|
||||
try testing.expect(data._arena != null);
|
||||
try testing.expectEqualStrings("42", data.a);
|
||||
@@ -1208,18 +1214,7 @@ test "parseIntoField: struct with basic fields" {
|
||||
try testing.expectEqual(84, data.value.b);
|
||||
try testing.expectEqual(24, data.value.c);
|
||||
|
||||
// Set with explicit default
|
||||
data.value = try parseAutoStruct(
|
||||
@TypeOf(data.value),
|
||||
alloc,
|
||||
"a:hello",
|
||||
.{ .a = "oh no", .b = 42 },
|
||||
);
|
||||
try testing.expectEqualStrings("hello", data.value.a);
|
||||
try testing.expectEqual(42, data.value.b);
|
||||
try testing.expectEqual(12, data.value.c);
|
||||
|
||||
// Missing required field
|
||||
// Missing require dfield
|
||||
try testing.expectError(
|
||||
error.InvalidValue,
|
||||
parseIntoField(@TypeOf(data), alloc, &data, "value", "a:hello"),
|
||||
@@ -1395,115 +1390,119 @@ test "ArgsIterator" {
|
||||
/// Returns an iterator (implements "next") that reads CLI args by line.
|
||||
/// Each CLI arg is expected to be a single line. This is used to implement
|
||||
/// configuration files.
|
||||
pub fn LineIterator(comptime ReaderType: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const LineIterator = struct {
|
||||
const Self = @This();
|
||||
|
||||
/// The maximum size a single line can be. We don't expect any
|
||||
/// CLI arg to exceed this size. Can't wait to git blame this in
|
||||
/// like 4 years and be wrong about this.
|
||||
pub const MAX_LINE_SIZE = 4096;
|
||||
/// The maximum size a single line can be. We don't expect any
|
||||
/// CLI arg to exceed this size. Can't wait to git blame this in
|
||||
/// like 4 years and be wrong about this.
|
||||
pub const MAX_LINE_SIZE = 4096;
|
||||
|
||||
/// Our stateful reader.
|
||||
r: ReaderType,
|
||||
/// Our stateful reader.
|
||||
r: *std.Io.Reader,
|
||||
|
||||
/// Filepath that is used for diagnostics. This is only used for
|
||||
/// diagnostic messages so it can be formatted however you want.
|
||||
/// It is prefixed to the messages followed by the line number.
|
||||
filepath: []const u8 = "",
|
||||
/// Filepath that is used for diagnostics. This is only used for
|
||||
/// diagnostic messages so it can be formatted however you want.
|
||||
/// It is prefixed to the messages followed by the line number.
|
||||
filepath: []const u8 = "",
|
||||
|
||||
/// The current line that we're on. This is 1-indexed because
|
||||
/// lines are generally 1-indexed in the real world. The value
|
||||
/// can be zero if we haven't read any lines yet.
|
||||
line: usize = 0,
|
||||
/// The current line that we're on. This is 1-indexed because
|
||||
/// lines are generally 1-indexed in the real world. The value
|
||||
/// can be zero if we haven't read any lines yet.
|
||||
line: usize = 0,
|
||||
|
||||
/// This is the buffer where we store the current entry that
|
||||
/// is formatted to be compatible with the parse function.
|
||||
entry: [MAX_LINE_SIZE]u8 = [_]u8{ '-', '-' } ++ ([_]u8{0} ** (MAX_LINE_SIZE - 2)),
|
||||
/// This is the buffer where we store the current entry that
|
||||
/// is formatted to be compatible with the parse function.
|
||||
entry: [MAX_LINE_SIZE]u8 = [_]u8{ '-', '-' } ++ ([_]u8{0} ** (MAX_LINE_SIZE - 2)),
|
||||
|
||||
pub fn next(self: *Self) ?[]const u8 {
|
||||
// TODO: detect "--" prefixed lines and give a friendlier error
|
||||
const buf = buf: {
|
||||
while (true) {
|
||||
// Read the full line
|
||||
var entry = self.r.readUntilDelimiterOrEof(self.entry[2..], '\n') catch |err| switch (err) {
|
||||
inline else => |e| {
|
||||
log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e });
|
||||
return null;
|
||||
},
|
||||
} orelse return null;
|
||||
pub fn init(reader: *std.Io.Reader) Self {
|
||||
return .{ .r = reader };
|
||||
}
|
||||
|
||||
// Increment our line counter
|
||||
self.line += 1;
|
||||
pub fn next(self: *Self) ?[]const u8 {
|
||||
// First prime the reader.
|
||||
// File readers at least are initialized with a size of 0,
|
||||
// and this will actually prompt the reader to get the actual
|
||||
// size of the file, which will be used in the EOF check below.
|
||||
//
|
||||
// This will also optimize reads down the line as we're
|
||||
// more likely to beworking with buffered data.
|
||||
self.r.fillMore() catch {};
|
||||
|
||||
// Trim any whitespace (including CR) around it
|
||||
const trim = std.mem.trim(u8, entry, whitespace ++ "\r");
|
||||
if (trim.len != entry.len) {
|
||||
std.mem.copyForwards(u8, entry, trim);
|
||||
entry = entry[0..trim.len];
|
||||
}
|
||||
var writer: std.Io.Writer = .fixed(self.entry[2..]);
|
||||
|
||||
// Ignore blank lines and comments
|
||||
if (entry.len == 0 or entry[0] == '#') continue;
|
||||
var entry = while (self.r.seek != self.r.end) {
|
||||
// Reset write head
|
||||
writer.end = 0;
|
||||
|
||||
// Trim spaces around '='
|
||||
if (mem.indexOf(u8, entry, "=")) |idx| {
|
||||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
const value = value: {
|
||||
var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
_ = self.r.streamDelimiterEnding(&writer, '\n') catch |e| {
|
||||
log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e });
|
||||
return null;
|
||||
};
|
||||
_ = self.r.discardDelimiterInclusive('\n') catch {};
|
||||
|
||||
// Detect a quoted string.
|
||||
if (value.len >= 2 and
|
||||
value[0] == '"' and
|
||||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Trim quotes since our CLI args processor expects
|
||||
// quotes to already be gone.
|
||||
value = value[1 .. value.len - 1];
|
||||
}
|
||||
var entry = writer.buffered();
|
||||
self.line += 1;
|
||||
|
||||
break :value value;
|
||||
};
|
||||
// Trim any whitespace (including CR) around it
|
||||
const trim = std.mem.trim(u8, entry, whitespace ++ "\r");
|
||||
if (trim.len != entry.len) {
|
||||
std.mem.copyForwards(u8, entry, trim);
|
||||
entry = entry[0..trim.len];
|
||||
}
|
||||
|
||||
const len = key.len + value.len + 1;
|
||||
if (entry.len != len) {
|
||||
std.mem.copyForwards(u8, entry, key);
|
||||
entry[key.len] = '=';
|
||||
std.mem.copyForwards(u8, entry[key.len + 1 ..], value);
|
||||
entry = entry[0..len];
|
||||
}
|
||||
}
|
||||
// Ignore blank lines and comments
|
||||
if (entry.len == 0 or entry[0] == '#') continue;
|
||||
break entry;
|
||||
} else return null;
|
||||
|
||||
break :buf entry;
|
||||
if (mem.indexOf(u8, entry, "=")) |idx| {
|
||||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
const value = value: {
|
||||
var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
|
||||
// Detect a quoted string.
|
||||
if (value.len >= 2 and
|
||||
value[0] == '"' and
|
||||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Trim quotes since our CLI args processor expects
|
||||
// quotes to already be gone.
|
||||
value = value[1 .. value.len - 1];
|
||||
}
|
||||
|
||||
break :value value;
|
||||
};
|
||||
|
||||
// We need to reslice so that we include our '--' at the beginning
|
||||
// of our buffer so that we can trick the CLI parser to treat it
|
||||
// as CLI args.
|
||||
return self.entry[0 .. buf.len + 2];
|
||||
const len = key.len + value.len + 1;
|
||||
if (entry.len != len) {
|
||||
std.mem.copyForwards(u8, entry, key);
|
||||
entry[key.len] = '=';
|
||||
std.mem.copyForwards(u8, entry[key.len + 1 ..], value);
|
||||
entry = entry[0..len];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a location for a diagnostic message.
|
||||
pub fn location(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error!?diags.Location {
|
||||
// If we have no filepath then we have no location.
|
||||
if (self.filepath.len == 0) return null;
|
||||
// We need to reslice so that we include our '--' at the beginning
|
||||
// of our buffer so that we can trick the CLI parser to treat it
|
||||
// as CLI args.
|
||||
return self.entry[0 .. entry.len + 2];
|
||||
}
|
||||
|
||||
return .{ .file = .{
|
||||
.path = try alloc.dupe(u8, self.filepath),
|
||||
.line = self.line,
|
||||
} };
|
||||
}
|
||||
};
|
||||
}
|
||||
/// Returns a location for a diagnostic message.
|
||||
pub fn location(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error!?diags.Location {
|
||||
// If we have no filepath then we have no location.
|
||||
if (self.filepath.len == 0) return null;
|
||||
|
||||
// Constructs a LineIterator (see docs for that).
|
||||
fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) {
|
||||
return .{ .r = reader };
|
||||
}
|
||||
return .{ .file = .{
|
||||
.path = try alloc.dupe(u8, self.filepath),
|
||||
.line = self.line,
|
||||
} };
|
||||
}
|
||||
};
|
||||
|
||||
/// An iterator valid for arg parsing from a slice.
|
||||
pub const SliceIterator = struct {
|
||||
@@ -1526,7 +1525,7 @@ pub fn sliceIterator(slice: []const []const u8) SliceIterator {
|
||||
|
||||
test "LineIterator" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\A
|
||||
\\B=42
|
||||
\\C
|
||||
@@ -1541,7 +1540,7 @@ test "LineIterator" {
|
||||
\\F= "value "
|
||||
);
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A", iter.next().?);
|
||||
try testing.expectEqualStrings("--B=42", iter.next().?);
|
||||
try testing.expectEqualStrings("--C", iter.next().?);
|
||||
@@ -1554,9 +1553,9 @@ test "LineIterator" {
|
||||
|
||||
test "LineIterator end in newline" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A\n\n");
|
||||
var reader: std.Io.Reader = .fixed("A\n\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
@@ -1564,9 +1563,9 @@ test "LineIterator end in newline" {
|
||||
|
||||
test "LineIterator spaces around '='" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A = B\n\n");
|
||||
var reader: std.Io.Reader = .fixed("A = B\n\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A=B", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
@@ -1574,18 +1573,18 @@ test "LineIterator spaces around '='" {
|
||||
|
||||
test "LineIterator no value" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A = \n\n");
|
||||
var reader: std.Io.Reader = .fixed("A = \n\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A=", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
}
|
||||
|
||||
test "LineIterator with CRLF line endings" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A\r\nB = C\r\n");
|
||||
var reader: std.Io.Reader = .fixed("A\r\nB = C\r\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A", iter.next().?);
|
||||
try testing.expectEqualStrings("--B=C", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
|
||||
@@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator;
|
||||
const help_strings = @import("help_strings");
|
||||
const vaxis = @import("vaxis");
|
||||
|
||||
const framedata = @import("framedata");
|
||||
const framedata = @embedFile("framedata");
|
||||
|
||||
const vxfw = vaxis.vxfw;
|
||||
|
||||
@@ -218,17 +218,20 @@ var frames: []const []const u8 = undefined;
|
||||
|
||||
/// Decompress the frames into a slice of individual frames
|
||||
fn decompressFrames(gpa: Allocator) !void {
|
||||
var fbs = std.io.fixedBufferStream(framedata.compressed);
|
||||
var list = std.ArrayList(u8).init(gpa);
|
||||
var src: std.Io.Reader = .fixed(framedata);
|
||||
|
||||
try std.compress.flate.decompress(fbs.reader(), list.writer());
|
||||
decompressed_data = try list.toOwnedSlice();
|
||||
// var buf: [std.compress.flate.max_window_len]u8 = undefined;
|
||||
var decompress: std.compress.flate.Decompress = .init(&src, .raw, &.{});
|
||||
|
||||
var frame_list = try std.ArrayList([]const u8).initCapacity(gpa, 235);
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
_ = try decompress.reader.streamRemaining(&out.writer);
|
||||
decompressed_data = try out.toOwnedSlice();
|
||||
|
||||
var frame_list: std.ArrayList([]const u8) = try .initCapacity(gpa, 235);
|
||||
|
||||
var frame_iter = std.mem.splitScalar(u8, decompressed_data, '\x01');
|
||||
while (frame_iter.next()) |frame| {
|
||||
try frame_list.append(frame);
|
||||
try frame_list.append(gpa, frame);
|
||||
}
|
||||
frames = try frame_list.toOwnedSlice();
|
||||
frames = try frame_list.toOwnedSlice(gpa);
|
||||
}
|
||||
|
||||
@@ -38,21 +38,35 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
try args.parse(Options, alloc_gpa, &opts, &iter);
|
||||
}
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
const result = runInner(alloc, &stdout_file, stdout);
|
||||
stdout.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runInner(
|
||||
alloc: Allocator,
|
||||
stdout_file: *std.fs.File,
|
||||
stdout: *std.Io.Writer,
|
||||
) !u8 {
|
||||
const crash_dir = try crash.defaultDir(alloc);
|
||||
var reports = std.ArrayList(crash.Report).init(alloc);
|
||||
var reports: std.ArrayList(crash.Report) = .empty;
|
||||
errdefer reports.deinit(alloc);
|
||||
|
||||
var it = try crash_dir.iterator();
|
||||
while (try it.next()) |report| try reports.append(.{
|
||||
while (try it.next()) |report| try reports.append(alloc, .{
|
||||
.name = try alloc.dupe(u8, report.name),
|
||||
.mtime = report.mtime,
|
||||
});
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
|
||||
// If we have no reports, then we're done. If we have a tty then we
|
||||
// print a message, otherwise we do nothing.
|
||||
if (reports.items.len == 0) {
|
||||
if (std.posix.isatty(stdout.handle)) {
|
||||
if (std.posix.isatty(stdout_file.handle)) {
|
||||
try stdout.writeAll("No crash reports! 👻\n");
|
||||
}
|
||||
return 0;
|
||||
@@ -60,16 +74,15 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
|
||||
std.mem.sort(crash.Report, reports.items, {}, lt);
|
||||
|
||||
const writer = stdout.writer();
|
||||
for (reports.items) |report| {
|
||||
var buf: [128]u8 = undefined;
|
||||
const now = std.time.nanoTimestamp();
|
||||
const diff = now - report.mtime;
|
||||
const since = if (diff <= 0) "now" else s: {
|
||||
const d = Config.Duration{ .duration = @intCast(diff) };
|
||||
break :s try std.fmt.bufPrint(&buf, "{s} ago", .{d.round(std.time.ns_per_s)});
|
||||
break :s try std.fmt.bufPrint(&buf, "{f} ago", .{d.round(std.time.ns_per_s)});
|
||||
};
|
||||
try writer.print("{s} ({s})\n", .{ report.name, since });
|
||||
try stdout.print("{s} ({s})\n", .{ report.name, since });
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -16,7 +16,7 @@ pub const Diagnostic = struct {
|
||||
message: [:0]const u8,
|
||||
|
||||
/// Write the full user-friendly diagnostic message to the writer.
|
||||
pub fn write(self: *const Diagnostic, writer: anytype) !void {
|
||||
pub fn format(self: *const Diagnostic, writer: *std.Io.Writer) !void {
|
||||
switch (self.location) {
|
||||
.none => {},
|
||||
.cli => |index| try writer.print("cli:{}:", .{index}),
|
||||
@@ -157,11 +157,14 @@ pub const DiagnosticList = struct {
|
||||
errdefer _ = self.list.pop();
|
||||
|
||||
if (comptime precompute_enabled) {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
try diag.write(buf.writer());
|
||||
var stream: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer stream.deinit();
|
||||
diag.format(&stream.writer) catch |err| switch (err) {
|
||||
// WriteFailed in this instance can only mean an OOM
|
||||
error.WriteFailed => return error.OutOfMemory,
|
||||
};
|
||||
|
||||
const owned: [:0]const u8 = try buf.toOwnedSliceSentinel(0);
|
||||
const owned: [:0]const u8 = try stream.toOwnedSliceSentinel(0);
|
||||
errdefer alloc.free(owned);
|
||||
|
||||
try self.precompute.messages.append(alloc, owned);
|
||||
|
||||
@@ -47,7 +47,9 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
// not using `exec` anymore and because this command isn't performance
|
||||
// critical where setting up the defer cleanup is a problem.
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
@@ -58,6 +60,13 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const result = runInner(alloc, stderr);
|
||||
// Flushing *shouldn't* fail but...
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
// We load the configuration once because that will write our
|
||||
// default configuration files to disk. We don't use the config.
|
||||
var config = try Config.load(alloc);
|
||||
@@ -133,23 +142,13 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
// so this is not a big deal.
|
||||
comptime assert(builtin.link_libc);
|
||||
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
errdefer buf.deinit(alloc);
|
||||
|
||||
const writer = buf.writer(alloc);
|
||||
var shellescape: internal_os.ShellEscapeWriter(std.ArrayListUnmanaged(u8).Writer) = .init(writer);
|
||||
var shellescapewriter = shellescape.writer();
|
||||
|
||||
try writer.writeAll(editor);
|
||||
try writer.writeByte(' ');
|
||||
try shellescapewriter.writeAll(path);
|
||||
|
||||
const command = try buf.toOwnedSliceSentinel(alloc, 0);
|
||||
defer alloc.free(command);
|
||||
|
||||
const editorZ = try alloc.dupeZ(u8, editor);
|
||||
defer alloc.free(editorZ);
|
||||
const pathZ = try alloc.dupeZ(u8, path);
|
||||
defer alloc.free(pathZ);
|
||||
const err = std.posix.execvpeZ(
|
||||
"sh",
|
||||
&.{ "sh", "-c", command },
|
||||
editorZ,
|
||||
&.{ editorZ, pathZ },
|
||||
std.c.environ,
|
||||
);
|
||||
|
||||
|
||||
@@ -107,12 +107,18 @@ pub const Action = enum {
|
||||
// for all commands by just changing this one place.
|
||||
|
||||
if (std.mem.eql(u8, field.name, @tagName(self))) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
const text = @field(help_strings.Action, field.name) ++ "\n";
|
||||
stdout.writeAll(text) catch |write_err| {
|
||||
std.log.warn("failed to write help text: {}\n", .{write_err});
|
||||
break :err 1;
|
||||
};
|
||||
stdout.flush() catch |flush_err| {
|
||||
std.log.warn("failed to flush help text: {}\n", .{flush_err});
|
||||
break :err 1;
|
||||
};
|
||||
|
||||
break :err 0;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [2048]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
try stdout.writeAll(
|
||||
\\Usage: ghostty [+action] [options]
|
||||
\\
|
||||
@@ -70,6 +72,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
\\where `<action>` is one of actions listed above.
|
||||
\\
|
||||
);
|
||||
try stdout.flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,15 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try helpgen_actions.generate(stdout, .plaintext, opts.docs, std.heap.page_allocator);
|
||||
var stdout: std.fs.File = .stdout();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
try helpgen_actions.generate(
|
||||
&stdout_writer.interface,
|
||||
.plaintext,
|
||||
opts.docs,
|
||||
std.heap.page_allocator,
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -39,11 +39,9 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
|
||||
var keys = std.ArrayList([]const u8).init(alloc);
|
||||
defer keys.deinit();
|
||||
for (x11_color.map.keys()) |key| try keys.append(key);
|
||||
var keys: std.ArrayList([]const u8) = .empty;
|
||||
defer keys.deinit(alloc);
|
||||
for (x11_color.map.keys()) |key| try keys.append(alloc, key);
|
||||
|
||||
std.mem.sortUnstable([]const u8, keys.items, {}, struct {
|
||||
fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
|
||||
@@ -52,12 +50,15 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
}.lessThan);
|
||||
|
||||
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
|
||||
var stdout: std.fs.File = .stdout();
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), keys.items);
|
||||
} else {
|
||||
const writer = stdout.writer();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
for (keys.items) |name| {
|
||||
const rgb = x11_color.map.get(name).?;
|
||||
try writer.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{
|
||||
@@ -74,19 +75,17 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
|
||||
fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
// Set up vaxis
|
||||
var tty = try vaxis.Tty.init();
|
||||
var buf: [1024]u8 = undefined;
|
||||
var tty = try vaxis.Tty.init(&buf);
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
defer vx.deinit(alloc, tty.anyWriter());
|
||||
defer vx.deinit(alloc, tty.writer());
|
||||
|
||||
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
|
||||
// event loop to auto-enable it.
|
||||
vx.caps.unicode = .unicode;
|
||||
try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
var buf_writer = tty.bufferedWriter();
|
||||
const writer = buf_writer.writer().any();
|
||||
try tty.writer().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.writer().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
const winsize: vaxis.Winsize = switch (builtin.os.tag) {
|
||||
// We use some default, it doesn't really matter for what
|
||||
@@ -100,7 +99,7 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
};
|
||||
try vx.resize(alloc, tty.anyWriter(), winsize);
|
||||
try vx.resize(alloc, tty.writer(), winsize);
|
||||
|
||||
const win = vx.window();
|
||||
|
||||
@@ -203,11 +202,8 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
}
|
||||
|
||||
// output the data
|
||||
try vx.prettyPrint(writer);
|
||||
try vx.prettyPrint(tty.writer());
|
||||
}
|
||||
|
||||
// be sure to flush!
|
||||
try buf_writer.flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,9 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
|
||||
// Its possible to build Ghostty without font discovery!
|
||||
if (comptime font.Discover == void) {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
try stderr.print(
|
||||
\\Ghostty was built without a font discovery mechanism. This is a compile-time
|
||||
\\option. Please review how Ghostty was built from source, contact the
|
||||
@@ -85,15 +87,18 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
,
|
||||
.{},
|
||||
);
|
||||
try stderr.flush();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [2048]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
// We'll be putting our fonts into a list categorized by family
|
||||
// so it is easier to read the output.
|
||||
var families = std.ArrayList([]const u8).init(alloc);
|
||||
var map = std.StringHashMap(std.ArrayListUnmanaged([]const u8)).init(alloc);
|
||||
var families: std.ArrayList([]const u8) = .empty;
|
||||
var map: std.StringHashMap(std.ArrayListUnmanaged([]const u8)) = .init(alloc);
|
||||
|
||||
// Look up all available fonts
|
||||
var disco = font.Discover.init();
|
||||
@@ -123,7 +128,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
|
||||
const gop = try map.getOrPut(family);
|
||||
if (!gop.found_existing) {
|
||||
try families.append(family);
|
||||
try families.append(alloc, family);
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
try gop.value_ptr.append(alloc, full_name);
|
||||
@@ -155,5 +160,6 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
try stdout.print("\n", .{});
|
||||
}
|
||||
|
||||
try stdout.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -64,27 +64,38 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
var config = if (opts.default) try Config.default(alloc) else try Config.load(alloc);
|
||||
defer config.deinit();
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const stdout: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
|
||||
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
|
||||
if (tui.can_pretty_print and !opts.plain and stdout.isTty()) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), config.keybind);
|
||||
} else {
|
||||
try config.keybind.formatEntryDocs(
|
||||
configpkg.entryFormatter("keybind", stdout.writer()),
|
||||
configpkg.entryFormatter("keybind", writer),
|
||||
opts.docs,
|
||||
);
|
||||
}
|
||||
|
||||
// Don't forget to flush!
|
||||
try writer.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TriggerList = std.SinglyLinkedList(Binding.Trigger);
|
||||
const TriggerNode = struct {
|
||||
data: Binding.Trigger,
|
||||
node: std.SinglyLinkedList.Node = .{},
|
||||
|
||||
pub fn get(node: *std.SinglyLinkedList.Node) *TriggerNode {
|
||||
return @fieldParentPtr("node", node);
|
||||
}
|
||||
};
|
||||
|
||||
const ChordBinding = struct {
|
||||
triggers: TriggerList,
|
||||
triggers: std.SinglyLinkedList,
|
||||
action: Binding.Action,
|
||||
|
||||
// Order keybinds based on various properties
|
||||
@@ -109,7 +120,8 @@ const ChordBinding = struct {
|
||||
const lhs_count: usize = blk: {
|
||||
var count: usize = 0;
|
||||
var maybe_trigger = lhs.triggers.first;
|
||||
while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) {
|
||||
while (maybe_trigger) |node| : (maybe_trigger = node.next) {
|
||||
const trigger: *TriggerNode = .get(node);
|
||||
if (trigger.data.mods.super) count += 1;
|
||||
if (trigger.data.mods.ctrl) count += 1;
|
||||
if (trigger.data.mods.shift) count += 1;
|
||||
@@ -120,7 +132,8 @@ const ChordBinding = struct {
|
||||
const rhs_count: usize = blk: {
|
||||
var count: usize = 0;
|
||||
var maybe_trigger = rhs.triggers.first;
|
||||
while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) {
|
||||
while (maybe_trigger) |node| : (maybe_trigger = node.next) {
|
||||
const trigger: *TriggerNode = .get(node);
|
||||
if (trigger.data.mods.super) count += 1;
|
||||
if (trigger.data.mods.ctrl) count += 1;
|
||||
if (trigger.data.mods.shift) count += 1;
|
||||
@@ -137,8 +150,8 @@ const ChordBinding = struct {
|
||||
var l_trigger = lhs.triggers.first;
|
||||
var r_trigger = rhs.triggers.first;
|
||||
while (l_trigger != null and r_trigger != null) {
|
||||
const l_int = l_trigger.?.data.mods.int();
|
||||
const r_int = r_trigger.?.data.mods.int();
|
||||
const l_int = TriggerNode.get(l_trigger.?).data.mods.int();
|
||||
const r_int = TriggerNode.get(r_trigger.?).data.mods.int();
|
||||
|
||||
if (l_int != r_int) {
|
||||
return l_int > r_int;
|
||||
@@ -154,13 +167,13 @@ const ChordBinding = struct {
|
||||
|
||||
while (l_trigger != null and r_trigger != null) {
|
||||
const lhs_key: c_int = blk: {
|
||||
switch (l_trigger.?.data.key) {
|
||||
switch (TriggerNode.get(l_trigger.?).data.key) {
|
||||
.physical => |key| break :blk @intFromEnum(key),
|
||||
.unicode => |key| break :blk @intCast(key),
|
||||
}
|
||||
};
|
||||
const rhs_key: c_int = blk: {
|
||||
switch (r_trigger.?.data.key) {
|
||||
switch (TriggerNode.get(r_trigger.?).data.key) {
|
||||
.physical => |key| break :blk @intFromEnum(key),
|
||||
.unicode => |key| break :blk @intCast(key),
|
||||
}
|
||||
@@ -186,19 +199,18 @@ const ChordBinding = struct {
|
||||
|
||||
fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
// Set up vaxis
|
||||
var tty = try vaxis.Tty.init();
|
||||
var buf: [1024]u8 = undefined;
|
||||
var tty = try vaxis.Tty.init(&buf);
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
defer vx.deinit(alloc, tty.anyWriter());
|
||||
const writer = tty.writer();
|
||||
defer vx.deinit(alloc, writer);
|
||||
|
||||
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
|
||||
// event loop to auto-enable it.
|
||||
vx.caps.unicode = .unicode;
|
||||
try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
var buf_writer = tty.bufferedWriter();
|
||||
const writer = buf_writer.writer().any();
|
||||
try writer.writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer writer.writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
const winsize: vaxis.Winsize = switch (builtin.os.tag) {
|
||||
// We use some default, it doesn't really matter for what
|
||||
@@ -212,7 +224,7 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
};
|
||||
try vx.resize(alloc, tty.anyWriter(), winsize);
|
||||
try vx.resize(alloc, writer, winsize);
|
||||
|
||||
const win = vx.window();
|
||||
|
||||
@@ -234,7 +246,9 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
|
||||
var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false };
|
||||
var maybe_trigger = bind.triggers.first;
|
||||
while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) {
|
||||
while (maybe_trigger) |node| : (maybe_trigger = node.next) {
|
||||
const trigger: *TriggerNode = .get(node);
|
||||
|
||||
if (trigger.data.mods.super) {
|
||||
result = win.printSegment(.{ .text = "super", .style = super_style }, .{ .col_offset = result.col });
|
||||
result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col });
|
||||
@@ -252,18 +266,18 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col });
|
||||
}
|
||||
const key = switch (trigger.data.key) {
|
||||
.physical => |k| try std.fmt.allocPrint(alloc, "{s}", .{@tagName(k)}),
|
||||
.physical => |k| try std.fmt.allocPrint(alloc, "{t}", .{k}),
|
||||
.unicode => |c| try std.fmt.allocPrint(alloc, "{u}", .{c}),
|
||||
};
|
||||
result = win.printSegment(.{ .text = key }, .{ .col_offset = result.col });
|
||||
|
||||
// Print a separator between chorded keys
|
||||
if (trigger.next != null) {
|
||||
if (trigger.node.next != null) {
|
||||
result = win.printSegment(.{ .text = " > ", .style = .{ .bold = true, .fg = .{ .index = 6 } } }, .{ .col_offset = result.col });
|
||||
}
|
||||
}
|
||||
|
||||
const action = try std.fmt.allocPrint(alloc, "{}", .{bind.action});
|
||||
const action = try std.fmt.allocPrint(alloc, "{f}", .{bind.action});
|
||||
// If our action has an argument, we print the argument in a different color
|
||||
if (std.mem.indexOfScalar(u8, action, ':')) |idx| {
|
||||
_ = win.print(&.{
|
||||
@@ -276,29 +290,33 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
}
|
||||
try vx.prettyPrint(writer);
|
||||
}
|
||||
try buf_writer.flush();
|
||||
try writer.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !struct { []ChordBinding, u16 } {
|
||||
fn iterateBindings(
|
||||
alloc: Allocator,
|
||||
iter: anytype,
|
||||
win: *const vaxis.Window,
|
||||
) !struct { []ChordBinding, u16 } {
|
||||
var widest_chord: u16 = 0;
|
||||
var bindings = std.ArrayList(ChordBinding).init(alloc);
|
||||
var bindings: std.ArrayList(ChordBinding) = .empty;
|
||||
while (iter.next()) |bind| {
|
||||
const width = blk: {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
const t = bind.key_ptr.*;
|
||||
|
||||
if (t.mods.super) try std.fmt.format(buf.writer(), "super + ", .{});
|
||||
if (t.mods.ctrl) try std.fmt.format(buf.writer(), "ctrl + ", .{});
|
||||
if (t.mods.alt) try std.fmt.format(buf.writer(), "alt + ", .{});
|
||||
if (t.mods.shift) try std.fmt.format(buf.writer(), "shift + ", .{});
|
||||
if (t.mods.super) try buf.writer.print("super + ", .{});
|
||||
if (t.mods.ctrl) try buf.writer.print("ctrl + ", .{});
|
||||
if (t.mods.alt) try buf.writer.print("alt + ", .{});
|
||||
if (t.mods.shift) try buf.writer.print("shift + ", .{});
|
||||
|
||||
switch (t.key) {
|
||||
.physical => |k| try std.fmt.format(buf.writer(), "{s}", .{@tagName(k)}),
|
||||
.unicode => |c| try std.fmt.format(buf.writer(), "{u}", .{c}),
|
||||
.physical => |k| try buf.writer.print("{t}", .{k}),
|
||||
.unicode => |c| try buf.writer.print("{u}", .{c}),
|
||||
}
|
||||
|
||||
break :blk win.gwidth(buf.items);
|
||||
break :blk win.gwidth(buf.written());
|
||||
};
|
||||
|
||||
switch (bind.value_ptr.*) {
|
||||
@@ -310,28 +328,28 @@ fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !s
|
||||
|
||||
// Prepend the current keybind onto the list of sub-binds
|
||||
for (sub_bindings) |*nb| {
|
||||
const prepend_node = try alloc.create(TriggerList.Node);
|
||||
prepend_node.* = TriggerList.Node{ .data = bind.key_ptr.* };
|
||||
nb.triggers.prepend(prepend_node);
|
||||
const prepend_node = try alloc.create(TriggerNode);
|
||||
prepend_node.* = .{ .data = bind.key_ptr.* };
|
||||
nb.triggers.prepend(&prepend_node.node);
|
||||
}
|
||||
|
||||
// Add the longest sub-bind width to the current bind width along with a padding
|
||||
// of 5 for the ' > ' spacer
|
||||
widest_chord = @max(widest_chord, width + max_width + 5);
|
||||
try bindings.appendSlice(sub_bindings);
|
||||
try bindings.appendSlice(alloc, sub_bindings);
|
||||
},
|
||||
.leaf => |leaf| {
|
||||
const node = try alloc.create(TriggerList.Node);
|
||||
node.* = TriggerList.Node{ .data = bind.key_ptr.* };
|
||||
const triggers = TriggerList{
|
||||
.first = node,
|
||||
};
|
||||
const node = try alloc.create(TriggerNode);
|
||||
node.* = .{ .data = bind.key_ptr.* };
|
||||
|
||||
widest_chord = @max(widest_chord, width);
|
||||
try bindings.append(.{ .triggers = triggers, .action = leaf.action });
|
||||
try bindings.append(alloc, .{
|
||||
.triggers = .{ .first = &node.node },
|
||||
.action = leaf.action,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return .{ try bindings.toOwnedSlice(), widest_chord };
|
||||
return .{ try bindings.toOwnedSlice(alloc), widest_chord };
|
||||
}
|
||||
|
||||
@@ -57,9 +57,12 @@ const ThemeListElement = struct {
|
||||
.host = .{ .raw = "" },
|
||||
.path = .{ .raw = self.path },
|
||||
};
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
errdefer buf.deinit();
|
||||
try uri.writeToStream(.{ .scheme = true, .authority = true, .path = true }, buf.writer());
|
||||
try uri.writeToStream(
|
||||
&buf.writer,
|
||||
.{ .scheme = true, .authority = true, .path = true },
|
||||
);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
};
|
||||
@@ -114,8 +117,14 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
var arena = std.heap.ArenaAllocator.init(gpa_alloc);
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var stdout_buf: [4096]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&stdout_buf);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buf: [4096]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&stderr_buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const resources_dir = global_state.resources_dir.app();
|
||||
if (resources_dir == null)
|
||||
@@ -124,9 +133,9 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
|
||||
var count: usize = 0;
|
||||
|
||||
var themes = std.ArrayList(ThemeListElement).init(alloc);
|
||||
var themes: std.ArrayList(ThemeListElement) = .empty;
|
||||
|
||||
var it = themepkg.LocationIterator{ .arena_alloc = arena.allocator() };
|
||||
var it: themepkg.LocationIterator = .{ .arena_alloc = arena.allocator() };
|
||||
|
||||
while (try it.next()) |loc| {
|
||||
var dir = std.fs.cwd().openDir(loc.dir, .{ .iterate = true }) catch |err| switch (err) {
|
||||
@@ -148,7 +157,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
count += 1;
|
||||
|
||||
const path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name });
|
||||
try themes.append(.{
|
||||
try themes.append(alloc, .{
|
||||
.path = path,
|
||||
.location = loc.location,
|
||||
.theme = try alloc.dupe(u8, entry.name),
|
||||
@@ -166,18 +175,20 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
|
||||
std.mem.sortUnstable(ThemeListElement, themes.items, {}, ThemeListElement.lessThan);
|
||||
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(std.io.getStdOut().handle)) {
|
||||
if (tui.can_pretty_print and !opts.plain and stdout_file.isTty()) {
|
||||
try preview(gpa_alloc, themes.items, opts.color);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (themes.items) |theme| {
|
||||
if (opts.path)
|
||||
try stdout.print("{s} ({s}) {s}\n", .{ theme.theme, @tagName(theme.location), theme.path })
|
||||
try stdout.print("{s} ({t}) {s}\n", .{ theme.theme, theme.location, theme.path })
|
||||
else
|
||||
try stdout.print("{s} ({s})\n", .{ theme.theme, @tagName(theme.location) });
|
||||
try stdout.print("{s} ({t})\n", .{ theme.theme, theme.location });
|
||||
}
|
||||
|
||||
// Don't forget to flush!
|
||||
try stdout.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -209,23 +220,28 @@ const Preview = struct {
|
||||
text_input: vaxis.widgets.TextInput,
|
||||
theme_filter: ColorScheme,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, themes: []ThemeListElement, theme_filter: ColorScheme) !*Preview {
|
||||
pub fn init(
|
||||
allocator: std.mem.Allocator,
|
||||
themes: []ThemeListElement,
|
||||
theme_filter: ColorScheme,
|
||||
buf: []u8,
|
||||
) !*Preview {
|
||||
const self = try allocator.create(Preview);
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.should_quit = false,
|
||||
.tty = try vaxis.Tty.init(),
|
||||
.tty = try .init(buf),
|
||||
.vx = try vaxis.init(allocator, .{}),
|
||||
.mouse = null,
|
||||
.themes = themes,
|
||||
.filtered = try std.ArrayList(usize).initCapacity(allocator, themes.len),
|
||||
.filtered = try .initCapacity(allocator, themes.len),
|
||||
.current = 0,
|
||||
.window = 0,
|
||||
.hex = false,
|
||||
.mode = .normal,
|
||||
.color_scheme = .light,
|
||||
.text_input = vaxis.widgets.TextInput.init(allocator, &self.vx.unicode),
|
||||
.text_input = .init(allocator, &self.vx.unicode),
|
||||
.theme_filter = theme_filter,
|
||||
};
|
||||
|
||||
@@ -236,9 +252,9 @@ const Preview = struct {
|
||||
|
||||
pub fn deinit(self: *Preview) void {
|
||||
const allocator = self.allocator;
|
||||
self.filtered.deinit();
|
||||
self.filtered.deinit(allocator);
|
||||
self.text_input.deinit();
|
||||
self.vx.deinit(allocator, self.tty.anyWriter());
|
||||
self.vx.deinit(allocator, self.tty.writer());
|
||||
self.tty.deinit();
|
||||
allocator.destroy(self);
|
||||
}
|
||||
@@ -251,12 +267,14 @@ const Preview = struct {
|
||||
try loop.init();
|
||||
try loop.start();
|
||||
|
||||
try self.vx.enterAltScreen(self.tty.anyWriter());
|
||||
try self.vx.setTitle(self.tty.anyWriter(), "👻 Ghostty Theme Preview 👻");
|
||||
try self.vx.queryTerminal(self.tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||
try self.vx.setMouseMode(self.tty.anyWriter(), true);
|
||||
const writer = self.tty.writer();
|
||||
|
||||
try self.vx.enterAltScreen(writer);
|
||||
try self.vx.setTitle(writer, "👻 Ghostty Theme Preview 👻");
|
||||
try self.vx.queryTerminal(writer, 1 * std.time.ns_per_s);
|
||||
try self.vx.setMouseMode(writer, true);
|
||||
if (self.vx.caps.color_scheme_updates)
|
||||
try self.vx.subscribeToColorSchemeUpdates(self.tty.anyWriter());
|
||||
try self.vx.subscribeToColorSchemeUpdates(writer);
|
||||
|
||||
while (!self.should_quit) {
|
||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||
@@ -269,9 +287,8 @@ const Preview = struct {
|
||||
}
|
||||
try self.draw(alloc);
|
||||
|
||||
var buffered = self.tty.bufferedWriter();
|
||||
try self.vx.render(buffered.writer().any());
|
||||
try buffered.flush();
|
||||
try self.vx.render(writer);
|
||||
try writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,11 +325,11 @@ const Preview = struct {
|
||||
const string = try std.ascii.allocLowerString(self.allocator, buffer);
|
||||
defer self.allocator.free(string);
|
||||
|
||||
var tokens = std.ArrayList([]const u8).init(self.allocator);
|
||||
defer tokens.deinit();
|
||||
var tokens: std.ArrayList([]const u8) = .empty;
|
||||
defer tokens.deinit(self.allocator);
|
||||
|
||||
var it = std.mem.tokenizeScalar(u8, string, ' ');
|
||||
while (it.next()) |token| try tokens.append(token);
|
||||
while (it.next()) |token| try tokens.append(self.allocator, token);
|
||||
|
||||
for (self.themes, 0..) |*theme, i| {
|
||||
try theme_config.loadFile(theme_config._arena.?.allocator(), theme.path);
|
||||
@@ -322,13 +339,13 @@ const Preview = struct {
|
||||
.to_lower = true,
|
||||
.plain = true,
|
||||
});
|
||||
if (theme.rank != null) try self.filtered.append(i);
|
||||
if (theme.rank != null) try self.filtered.append(self.allocator, i);
|
||||
}
|
||||
} else {
|
||||
for (self.themes, 0..) |*theme, i| {
|
||||
try theme_config.loadFile(theme_config._arena.?.allocator(), theme.path);
|
||||
if (shouldIncludeTheme(self.theme_filter, theme_config)) {
|
||||
try self.filtered.append(i);
|
||||
try self.filtered.append(self.allocator, i);
|
||||
theme.rank = null;
|
||||
}
|
||||
}
|
||||
@@ -421,13 +438,13 @@ const Preview = struct {
|
||||
self.hex = false;
|
||||
if (key.matches('c', .{}))
|
||||
try self.vx.copyToSystemClipboard(
|
||||
self.tty.anyWriter(),
|
||||
self.tty.writer(),
|
||||
self.themes[self.filtered.items[self.current]].theme,
|
||||
alloc,
|
||||
)
|
||||
else if (key.matches('c', .{ .shift = true }))
|
||||
try self.vx.copyToSystemClipboard(
|
||||
self.tty.anyWriter(),
|
||||
self.tty.writer(),
|
||||
self.themes[self.filtered.items[self.current]].path,
|
||||
alloc,
|
||||
);
|
||||
@@ -471,7 +488,7 @@ const Preview = struct {
|
||||
},
|
||||
.color_scheme => |color_scheme| self.color_scheme = color_scheme,
|
||||
.mouse => |mouse| self.mouse = mouse,
|
||||
.winsize => |ws| try self.vx.resize(self.allocator, self.tty.anyWriter(), ws),
|
||||
.winsize => |ws| try self.vx.resize(self.allocator, self.tty.writer(), ws),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,14 +1061,14 @@ const Preview = struct {
|
||||
);
|
||||
}
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
for (config._diagnostics.items(), 0..) |diag, captured_i| {
|
||||
const i: u16 = @intCast(captured_i);
|
||||
try diag.write(buf.writer());
|
||||
try diag.format(&buf.writer);
|
||||
_ = child.printSegment(
|
||||
.{
|
||||
.text = buf.items,
|
||||
.text = buf.written(),
|
||||
.style = self.ui_err(),
|
||||
},
|
||||
.{
|
||||
@@ -1319,7 +1336,7 @@ const Preview = struct {
|
||||
.{ .text = "const ", .style = color5 },
|
||||
.{ .text = "stdout ", .style = standard },
|
||||
.{ .text = "=", .style = color5 },
|
||||
.{ .text = " std.io.getStdOut().writer();", .style = standard },
|
||||
.{ .text = " std.Io.getStdOut().writer();", .style = standard },
|
||||
},
|
||||
.{
|
||||
.row_offset = 7,
|
||||
@@ -1651,7 +1668,13 @@ fn color(config: Config, palette: usize) vaxis.Color {
|
||||
const lorem_ipsum = @embedFile("lorem_ipsum.txt");
|
||||
|
||||
fn preview(allocator: std.mem.Allocator, themes: []ThemeListElement, theme_filter: ColorScheme) !void {
|
||||
var app = try Preview.init(allocator, themes, theme_filter);
|
||||
var buf: [4096]u8 = undefined;
|
||||
var app = try Preview.init(
|
||||
allocator,
|
||||
themes,
|
||||
theme_filter,
|
||||
&buf,
|
||||
);
|
||||
defer app.deinit();
|
||||
try app.run();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub const Options = struct {
|
||||
// If it's not `-e` continue with the standard argument parsning.
|
||||
if (!std.mem.eql(u8, arg, "-e")) return true;
|
||||
|
||||
var arguments: std.ArrayListUnmanaged([:0]const u8) = .empty;
|
||||
var arguments: std.ArrayList([:0]const u8) = .empty;
|
||||
errdefer {
|
||||
for (arguments.items) |argument| alloc.free(argument);
|
||||
arguments.deinit(alloc);
|
||||
@@ -99,12 +99,21 @@ pub const Options = struct {
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
defer iter.deinit();
|
||||
return try runArgs(alloc, &iter);
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runArgs(alloc, &iter, stderr);
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
fn runArgs(
|
||||
alloc_gpa: Allocator,
|
||||
argsIter: anytype,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
|
||||
@@ -126,9 +135,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
inner: inline for (@typeInfo(Options).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue :inner;
|
||||
if (std.mem.eql(u8, field.name, diagnostic.key)) {
|
||||
try stderr.writeAll("config error: ");
|
||||
try diagnostic.write(stderr);
|
||||
try stderr.writeAll("\n");
|
||||
try stderr.print("config error: {f}\n", .{diagnostic});
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,10 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
|
||||
// For some reason `std.fmt.format` isn't working here but it works in
|
||||
// tests so we just do configfmt.format.
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try configfmt.format("", .{}, stdout);
|
||||
var stdout: std.fs.File = .stdout();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
try configfmt.format(&stdout_writer.interface);
|
||||
try stdout_writer.end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -64,13 +64,32 @@ pub const Options = struct {
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
defer iter.deinit();
|
||||
return try runArgs(alloc, &iter);
|
||||
|
||||
var stdout_buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stdout().writer(&stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runArgs(
|
||||
alloc,
|
||||
&iter,
|
||||
stdout,
|
||||
stderr,
|
||||
);
|
||||
stdout.flush() catch {};
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
fn runArgs(
|
||||
alloc_gpa: Allocator,
|
||||
argsIter: anytype,
|
||||
stdout: *std.Io.Writer,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
// Its possible to build Ghostty without font discovery!
|
||||
if (comptime font.Discover == void) {
|
||||
try stderr.print(
|
||||
@@ -104,9 +123,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
inner: inline for (@typeInfo(Options).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue :inner;
|
||||
if (std.mem.eql(u8, field.name, diagnostic.key)) {
|
||||
try stderr.writeAll("config error: ");
|
||||
try diagnostic.write(stderr);
|
||||
try stderr.writeAll("\n");
|
||||
try stderr.print("config error: {f}\n", .{diagnostic});
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
@@ -138,9 +155,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
if (field.name[0] == '_') continue :inner;
|
||||
if (std.mem.eql(u8, field.name, diagnostic.key) and (diagnostic.location == .none or diagnostic.location == .cli)) continue :outer;
|
||||
}
|
||||
try stderr.writeAll("config error: ");
|
||||
try diagnostic.write(stderr);
|
||||
try stderr.writeAll("\n");
|
||||
try stderr.print("config error: {f}\n", .{diagnostic});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,8 +204,8 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
|
||||
fn lookup(
|
||||
alloc: std.mem.Allocator,
|
||||
stdout: anytype,
|
||||
stderr: anytype,
|
||||
stdout: *std.Io.Writer,
|
||||
stderr: *std.Io.Writer,
|
||||
font_grid: *font.SharedGrid,
|
||||
style: font.Style,
|
||||
presentation: ?font.Presentation,
|
||||
|
||||
@@ -57,8 +57,6 @@ pub fn clear(self: DiskCache) !void {
|
||||
|
||||
pub const AddResult = enum { added, updated };
|
||||
|
||||
pub const AddError = std.fs.Dir.MakeError || std.fs.File.OpenError || std.fs.File.LockError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked };
|
||||
|
||||
/// Add or update a hostname entry in the cache.
|
||||
/// Returns AddResult.added for new entries or AddResult.updated for existing ones.
|
||||
/// The cache file is created if it doesn't exist with secure permissions (0600).
|
||||
@@ -66,7 +64,7 @@ pub fn add(
|
||||
self: DiskCache,
|
||||
alloc: Allocator,
|
||||
hostname: []const u8,
|
||||
) AddError!AddResult {
|
||||
) !AddResult {
|
||||
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
|
||||
|
||||
// Create cache directory if needed
|
||||
@@ -130,15 +128,13 @@ pub fn add(
|
||||
return result;
|
||||
}
|
||||
|
||||
pub const RemoveError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked };
|
||||
|
||||
/// Remove a hostname entry from the cache.
|
||||
/// No error is returned if the hostname doesn't exist or the cache file is missing.
|
||||
pub fn remove(
|
||||
self: DiskCache,
|
||||
alloc: Allocator,
|
||||
hostname: []const u8,
|
||||
) RemoveError!void {
|
||||
) !void {
|
||||
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
|
||||
|
||||
// Open our file
|
||||
@@ -199,7 +195,7 @@ pub fn contains(
|
||||
return entries.contains(hostname);
|
||||
}
|
||||
|
||||
fn fixupPermissions(file: std.fs.File) !void {
|
||||
fn fixupPermissions(file: std.fs.File) (std.fs.File.StatError || std.fs.File.ChmodError)!void {
|
||||
// Windows does not support chmod
|
||||
if (comptime builtin.os.tag == .windows) return;
|
||||
|
||||
@@ -211,14 +207,12 @@ fn fixupPermissions(file: std.fs.File) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub const WriteCacheFileError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.WriteError || std.fs.Dir.RealPathAllocError || std.posix.RealPathError || std.posix.RenameError || error{FileTooBig};
|
||||
|
||||
fn writeCacheFile(
|
||||
self: DiskCache,
|
||||
alloc: Allocator,
|
||||
entries: std.StringHashMap(Entry),
|
||||
expire_days: ?u32,
|
||||
) WriteCacheFileError!void {
|
||||
) !void {
|
||||
var td: TempDir = try .init();
|
||||
defer td.deinit();
|
||||
|
||||
@@ -227,14 +221,18 @@ fn writeCacheFile(
|
||||
const tmp_path = try td.dir.realpathAlloc(alloc, "ssh-cache");
|
||||
defer alloc.free(tmp_path);
|
||||
|
||||
const writer = tmp_file.writer();
|
||||
var buf: [1024]u8 = undefined;
|
||||
var writer = tmp_file.writer(&buf);
|
||||
var iter = entries.iterator();
|
||||
while (iter.next()) |kv| {
|
||||
// Only write non-expired entries
|
||||
if (kv.value_ptr.isExpired(expire_days)) continue;
|
||||
try kv.value_ptr.format(writer);
|
||||
try kv.value_ptr.format(&writer.interface);
|
||||
}
|
||||
|
||||
// Don't forget to flush!!
|
||||
try writer.interface.flush();
|
||||
|
||||
// Atomic replace
|
||||
try std.fs.renameAbsolute(tmp_path, self.path);
|
||||
}
|
||||
@@ -278,8 +276,12 @@ pub fn deinitEntries(
|
||||
fn readEntries(
|
||||
alloc: Allocator,
|
||||
file: std.fs.File,
|
||||
) (std.fs.File.ReadError || Allocator.Error || error{FileTooBig})!std.StringHashMap(Entry) {
|
||||
const content = try file.readToEndAlloc(alloc, MAX_CACHE_SIZE);
|
||||
) !std.StringHashMap(Entry) {
|
||||
var reader = file.reader(&.{});
|
||||
const content = try reader.interface.allocRemaining(
|
||||
alloc,
|
||||
.limited(MAX_CACHE_SIZE),
|
||||
);
|
||||
defer alloc.free(content);
|
||||
|
||||
var entries = std.StringHashMap(Entry).init(alloc);
|
||||
@@ -403,10 +405,12 @@ test "disk cache clear" {
|
||||
// Create our path
|
||||
var td: TempDir = try .init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll("HELLO!");
|
||||
var file_writer = file.writer(&buf);
|
||||
try file_writer.interface.writeAll("HELLO!");
|
||||
}
|
||||
const path = try td.dir.realpathAlloc(alloc, "cache");
|
||||
defer alloc.free(path);
|
||||
@@ -429,10 +433,14 @@ test "disk cache operations" {
|
||||
// Create our path
|
||||
var td: TempDir = try .init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll("HELLO!");
|
||||
var file_writer = file.writer(&buf);
|
||||
const writer = &file_writer.interface;
|
||||
try writer.writeAll("HELLO!");
|
||||
try writer.flush();
|
||||
}
|
||||
const path = try td.dir.realpathAlloc(alloc, "cache");
|
||||
defer alloc.free(path);
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn parse(line: []const u8) ?Entry {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(self: Entry, writer: anytype) !void {
|
||||
pub fn format(self: Entry, writer: *std.Io.Writer) !void {
|
||||
try writer.print(
|
||||
"{s}|{d}|{s}\n",
|
||||
.{ self.hostname, self.timestamp, self.terminfo_version },
|
||||
|
||||
@@ -61,9 +61,30 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
try args.parse(Options, alloc_gpa, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var stdout_buffer: [1024]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&stdout_buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_file: std.fs.File = .stderr();
|
||||
var stderr_writer = stderr_file.writer(&stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runInner(alloc, opts, stdout, stderr);
|
||||
|
||||
// Flushing *shouldn't* fail but...
|
||||
stdout.flush() catch {};
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn runInner(
|
||||
alloc: Allocator,
|
||||
opts: Options,
|
||||
stdout: *std.Io.Writer,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
// Setup our disk cache to the standard location
|
||||
const cache_path = try DiskCache.defaultPath(alloc, "ghostty");
|
||||
const cache: DiskCache = .{ .path = cache_path };
|
||||
@@ -165,7 +186,7 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
fn listEntries(
|
||||
alloc: Allocator,
|
||||
entries: *const std.StringHashMap(Entry),
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
if (entries.count() == 0) {
|
||||
try writer.print("No hosts in cache.\n", .{});
|
||||
@@ -173,12 +194,12 @@ fn listEntries(
|
||||
}
|
||||
|
||||
// Sort entries by hostname for consistent output
|
||||
var items = std.ArrayList(Entry).init(alloc);
|
||||
defer items.deinit();
|
||||
var items: std.ArrayList(Entry) = .empty;
|
||||
defer items.deinit(alloc);
|
||||
|
||||
var iter = entries.iterator();
|
||||
while (iter.next()) |kv| {
|
||||
try items.append(kv.value_ptr.*);
|
||||
try items.append(alloc, kv.value_ptr.*);
|
||||
}
|
||||
|
||||
std.mem.sort(Entry, items.items, {}, struct {
|
||||
|
||||
@@ -40,8 +40,19 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
const result = runInner(alloc, opts, stdout);
|
||||
try stdout_writer.end();
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runInner(
|
||||
alloc: std.mem.Allocator,
|
||||
opts: Options,
|
||||
stdout: *std.Io.Writer,
|
||||
) !u8 {
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
@@ -58,15 +69,9 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
||||
try cfg.finalize();
|
||||
|
||||
if (cfg._diagnostics.items().len > 0) {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
for (cfg._diagnostics.items()) |diag| {
|
||||
try diag.write(buf.writer());
|
||||
try stdout.print("{s}\n", .{buf.items});
|
||||
buf.clearRetainingCapacity();
|
||||
try stdout.print("{f}\n", .{diag});
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,12 @@ pub const Options = struct {};
|
||||
/// The `version` command is used to display information about Ghostty. Recognized as
|
||||
/// either `+version` or `--version`.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const tty = std.io.getStdOut().isTty();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&buffer);
|
||||
|
||||
const stdout = &stdout_writer.interface;
|
||||
const tty = stdout_file.isTty();
|
||||
|
||||
if (tty) if (build_config.version.build) |commit_hash| {
|
||||
try stdout.print(
|
||||
@@ -29,7 +33,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
|
||||
try stdout.print("Version\n", .{});
|
||||
try stdout.print(" - version: {s}\n", .{build_config.version_string});
|
||||
try stdout.print(" - channel: {s}\n", .{@tagName(build_config.release_channel)});
|
||||
try stdout.print(" - channel: {t}\n", .{build_config.release_channel});
|
||||
|
||||
try stdout.print("Build Config\n", .{});
|
||||
try stdout.print(" - Zig version : {s}\n", .{builtin.zig_version_string});
|
||||
@@ -37,20 +41,20 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try stdout.print(" - app runtime : {}\n", .{build_config.app_runtime});
|
||||
try stdout.print(" - font engine : {}\n", .{build_config.font_backend});
|
||||
try stdout.print(" - renderer : {}\n", .{renderer.Renderer});
|
||||
try stdout.print(" - libxev : {s}\n", .{@tagName(xev.backend)});
|
||||
try stdout.print(" - libxev : {t}\n", .{xev.backend});
|
||||
if (comptime build_config.app_runtime == .gtk) {
|
||||
if (comptime builtin.os.tag == .linux) {
|
||||
const kernel_info = internal_os.getKernelInfo(alloc);
|
||||
defer if (kernel_info) |k| alloc.free(k);
|
||||
try stdout.print(" - kernel version: {s}\n", .{kernel_info orelse "Kernel information unavailable"});
|
||||
}
|
||||
try stdout.print(" - desktop env : {s}\n", .{@tagName(internal_os.desktopEnvironment())});
|
||||
try stdout.print(" - desktop env : {t}\n", .{internal_os.desktopEnvironment()});
|
||||
try stdout.print(" - GTK version :\n", .{});
|
||||
try stdout.print(" build : {}\n", .{gtk_version.comptime_version});
|
||||
try stdout.print(" runtime : {}\n", .{gtk_version.getRuntimeVersion()});
|
||||
try stdout.print(" build : {f}\n", .{gtk_version.comptime_version});
|
||||
try stdout.print(" runtime : {f}\n", .{gtk_version.getRuntimeVersion()});
|
||||
try stdout.print(" - libadwaita : enabled\n", .{});
|
||||
try stdout.print(" build : {}\n", .{adw_version.comptime_version});
|
||||
try stdout.print(" runtime : {}\n", .{adw_version.getRuntimeVersion()});
|
||||
try stdout.print(" build : {f}\n", .{adw_version.comptime_version});
|
||||
try stdout.print(" runtime : {f}\n", .{adw_version.getRuntimeVersion()});
|
||||
if (comptime build_options.x11) {
|
||||
try stdout.print(" - libX11 : enabled\n", .{});
|
||||
} else {
|
||||
@@ -65,5 +69,8 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try stdout.print(" - libwayland : disabled\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// Don't forget to flush!
|
||||
try stdout.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user