diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/cli/diagnostics.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (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.zig | 197 |
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; + } +}; |
