This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195const std = @import("std");const builtin = @import("builtin");const assert = std.debug.assert;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 write(self: *const Diagnostic, writer: anytype) !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 buf = std.ArrayList(u8).init(alloc); defer buf.deinit(); try diag.write(buf.writer());
const owned: [:0]const u8 = try buf.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; }};