aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/cli/diagnostics.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/cli/diagnostics.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/cli/diagnostics.zig')
-rw-r--r--vendor/libghostty-vt/src/cli/diagnostics.zig197
1 files changed, 197 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/cli/diagnostics.zig b/vendor/libghostty-vt/src/cli/diagnostics.zig
new file mode 100644
index 00000000..7f4dcc45
--- /dev/null
+++ b/vendor/libghostty-vt/src/cli/diagnostics.zig
@@ -0,0 +1,197 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const assert = @import("../quirks.zig").inlineAssert;
+const Allocator = std.mem.Allocator;
+const build_config = @import("../build_config.zig");
+
+/// A diagnostic message from parsing. This is used to provide additional
+/// human-friendly warnings and errors about the parsed data.
+///
+/// All of the memory for the diagnostic is allocated from the arena
+/// associated with the config structure. If an arena isn't available
+/// then diagnostics are not supported.
+pub const Diagnostic = struct {
+ location: Location = .none,
+ key: [:0]const u8 = "",
+ message: [:0]const u8,
+
+ /// Write the full user-friendly diagnostic message to the writer.
+ pub fn format(self: *const Diagnostic, writer: *std.Io.Writer) !void {
+ switch (self.location) {
+ .none => {},
+ .cli => |index| try writer.print("cli:{}:", .{index}),
+ .file => |file| try writer.print(
+ "{s}:{}:",
+ .{ file.path, file.line },
+ ),
+ }
+
+ if (self.key.len > 0) {
+ try writer.print("{s}: ", .{self.key});
+ } else if (self.location != .none) {
+ try writer.print(" ", .{});
+ }
+
+ try writer.print("{s}", .{self.message});
+ }
+
+ pub fn clone(self: *const Diagnostic, alloc: Allocator) Allocator.Error!Diagnostic {
+ return .{
+ .location = try self.location.clone(alloc),
+ .key = try alloc.dupeZ(u8, self.key),
+ .message = try alloc.dupeZ(u8, self.message),
+ };
+ }
+};
+
+/// The possible locations for a diagnostic message. This is used
+/// to provide context for the message.
+pub const Location = union(enum) {
+ none,
+ cli: usize,
+ file: struct {
+ path: []const u8,
+ line: usize,
+ },
+
+ pub const Key = @typeInfo(Location).@"union".tag_type.?;
+
+ pub fn fromIter(iter: anytype, alloc: Allocator) Allocator.Error!Location {
+ const Iter = t: {
+ const T = @TypeOf(iter);
+ break :t switch (@typeInfo(T)) {
+ .pointer => |v| v.child,
+ .@"struct" => T,
+ else => return .none,
+ };
+ };
+
+ if (!@hasDecl(Iter, "location")) return .none;
+ return (try iter.location(alloc)) orelse .none;
+ }
+
+ pub fn clone(self: *const Location, alloc: Allocator) Allocator.Error!Location {
+ return switch (self.*) {
+ .none,
+ .cli,
+ => self.*,
+
+ .file => |v| .{ .file = .{
+ .path = try alloc.dupe(u8, v.path),
+ .line = v.line,
+ } },
+ };
+ }
+};
+
+/// A list of diagnostics. The "_diagnostics" field must be this type
+/// for diagnostics to be supported. If this field is an incorrect type
+/// a compile-time error will be raised.
+///
+/// This is implemented as a simple wrapper around an array list
+/// so that we can inject some logic around adding diagnostics
+/// and potentially in the future structure them differently.
+pub const DiagnosticList = struct {
+ /// The list of diagnostics.
+ list: std.ArrayListUnmanaged(Diagnostic) = .{},
+
+ /// Precomputed data for diagnostics. This is used specifically
+ /// when we build libghostty so that we can precompute the messages
+ /// and return them via the C API without allocating memory at
+ /// call time.
+ precompute: Precompute = precompute_init,
+
+ const precompute_enabled = switch (build_config.artifact) {
+ // We enable precompute for tests so that the logic is
+ // semantically analyzed and run.
+ .exe, .wasm_module => builtin.is_test,
+
+ // We specifically want precompute for libghostty.
+ .lib => true,
+ };
+
+ const Precompute = if (precompute_enabled) struct {
+ messages: std.ArrayListUnmanaged([:0]const u8) = .{},
+
+ pub fn clone(
+ self: *const Precompute,
+ alloc: Allocator,
+ ) Allocator.Error!Precompute {
+ var result: Precompute = .{};
+ try result.messages.ensureTotalCapacity(alloc, self.messages.items.len);
+ for (self.messages.items) |msg| {
+ result.messages.appendAssumeCapacity(
+ try alloc.dupeZ(u8, msg),
+ );
+ }
+ return result;
+ }
+ } else void;
+
+ const precompute_init: Precompute = if (precompute_enabled) .{} else {};
+
+ pub fn clone(
+ self: *const DiagnosticList,
+ alloc: Allocator,
+ ) Allocator.Error!DiagnosticList {
+ var result: DiagnosticList = .{};
+
+ try result.list.ensureTotalCapacity(alloc, self.list.items.len);
+ for (self.list.items) |*diag| result.list.appendAssumeCapacity(
+ try diag.clone(alloc),
+ );
+
+ if (comptime precompute_enabled) {
+ result.precompute = try self.precompute.clone(alloc);
+ }
+
+ return result;
+ }
+
+ pub fn append(
+ self: *DiagnosticList,
+ alloc: Allocator,
+ diag: Diagnostic,
+ ) Allocator.Error!void {
+ try self.list.append(alloc, diag);
+ errdefer _ = self.list.pop();
+
+ if (comptime precompute_enabled) {
+ 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 stream.toOwnedSliceSentinel(0);
+ errdefer alloc.free(owned);
+
+ try self.precompute.messages.append(alloc, owned);
+ errdefer _ = self.precompute.messages.pop();
+
+ assert(self.precompute.messages.items.len == self.list.items.len);
+ }
+ }
+
+ pub fn empty(self: *const DiagnosticList) bool {
+ return self.list.items.len == 0;
+ }
+
+ pub fn items(self: *const DiagnosticList) []const Diagnostic {
+ return self.list.items;
+ }
+
+ /// Returns true if there are any diagnostics for the given
+ /// location type.
+ pub fn containsLocation(
+ self: *const DiagnosticList,
+ location: Location.Key,
+ ) bool {
+ for (self.list.items) |diag| {
+ if (diag.location == location) return true;
+ }
+
+ return false;
+ }
+};