| ... | @@ -344,6 +344,20 @@ pub const AllErrors = struct { | ... | @@ -344,6 +344,20 @@ pub const AllErrors = struct { |
| 344 | /// Does not include the trailing newline. | 344 | /// Does not include the trailing newline. |
| 345 | source_line: ?[]const u8, | 345 | source_line: ?[]const u8, |
| 346 | notes: []Message = &.{}, | 346 | notes: []Message = &.{}, |
| | 347 | |
| | 348 | /// Splits the error message up into lines to properly indent them |
| | 349 | /// to allow for long, good-looking error messages. |
| | 350 | /// |
| | 351 | /// This is used to split the message in `@compileError("hello\nworld")` for example. |
| | 352 | fn writeMsg(src: @This(), stderr: anytype, indent: usize) !void { |
| | 353 | var lines = mem.split(u8, src.msg, "\n"); |
| | 354 | while (lines.next()) |line| { |
| | 355 | try stderr.writeAll(line); |
| | 356 | if (lines.index == null) break; |
| | 357 | try stderr.writeByte('\n'); |
| | 358 | try stderr.writeByteNTimes(' ', indent); |
| | 359 | } |
| | 360 | } |
| 347 | }, | 361 | }, |
| 348 | plain: struct { | 362 | plain: struct { |
| 349 | msg: []const u8, | 363 | msg: []const u8, |
| ... | @@ -367,35 +381,41 @@ pub const AllErrors = struct { | ... | @@ -367,35 +381,41 @@ pub const AllErrors = struct { |
| 367 | std.debug.getStderrMutex().lock(); | 381 | std.debug.getStderrMutex().lock(); |
| 368 | defer std.debug.getStderrMutex().unlock(); | 382 | defer std.debug.getStderrMutex().unlock(); |
| 369 | const stderr = std.io.getStdErr(); | 383 | const stderr = std.io.getStdErr(); |
| 370 | return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red, 0) catch return; | 384 | return msg.renderToWriter(ttyconf, stderr.writer(), "error", .Red, 0) catch return; |
| 371 | } | 385 | } |
| 372 | | 386 | |
| 373 | fn renderToStdErrInner( | 387 | pub fn renderToWriter( |
| 374 | msg: Message, | 388 | msg: Message, |
| 375 | ttyconf: std.debug.TTY.Config, | 389 | ttyconf: std.debug.TTY.Config, |
| 376 | stderr_file: std.fs.File, | 390 | stderr: anytype, |
| 377 | kind: []const u8, | 391 | kind: []const u8, |
| 378 | color: std.debug.TTY.Color, | 392 | color: std.debug.TTY.Color, |
| 379 | indent: usize, | 393 | indent: usize, |
| 380 | ) anyerror!void { | 394 | ) anyerror!void { |
| 381 | const stderr = stderr_file.writer(); | 395 | var counting_writer = std.io.countingWriter(stderr); |
| | 396 | const counting_stderr = counting_writer.writer(); |
| 382 | switch (msg) { | 397 | switch (msg) { |
| 383 | .src => |src| { | 398 | .src => |src| { |
| 384 | try stderr.writeByteNTimes(' ', indent); | 399 | try counting_stderr.writeByteNTimes(' ', indent); |
| 385 | ttyconf.setColor(stderr, .Bold); | 400 | ttyconf.setColor(stderr, .Bold); |
| 386 | try stderr.print("{s}:{d}:{d}: ", .{ | 401 | try counting_stderr.print("{s}:{d}:{d}: ", .{ |
| 387 | src.src_path, | 402 | src.src_path, |
| 388 | src.line + 1, | 403 | src.line + 1, |
| 389 | src.column + 1, | 404 | src.column + 1, |
| 390 | }); | 405 | }); |
| 391 | ttyconf.setColor(stderr, color); | 406 | ttyconf.setColor(stderr, color); |
| 392 | try stderr.writeAll(kind); | 407 | try counting_stderr.writeAll(kind); |
| | 408 | try counting_stderr.writeAll(": "); |
| | 409 | // This is the length of the part before the error message: |
| | 410 | // e.g. "file.zig:4:5: error: " |
| | 411 | const prefix_len = @intCast(usize, counting_stderr.context.bytes_written); |
| 393 | ttyconf.setColor(stderr, .Reset); | 412 | ttyconf.setColor(stderr, .Reset); |
| 394 | ttyconf.setColor(stderr, .Bold); | 413 | ttyconf.setColor(stderr, .Bold); |
| 395 | if (src.count == 1) { | 414 | if (src.count == 1) { |
| 396 | try stderr.print(" {s}\n", .{src.msg}); | 415 | try src.writeMsg(stderr, prefix_len); |
| | 416 | try stderr.writeByte('\n'); |
| 397 | } else { | 417 | } else { |
| 398 | try stderr.print(" {s}", .{src.msg}); | 418 | try src.writeMsg(stderr, prefix_len); |
| 399 | ttyconf.setColor(stderr, .Dim); | 419 | ttyconf.setColor(stderr, .Dim); |
| 400 | try stderr.print(" ({d} times)\n", .{src.count}); | 420 | try stderr.print(" ({d} times)\n", .{src.count}); |
| 401 | } | 421 | } |
| ... | @@ -414,24 +434,25 @@ pub const AllErrors = struct { | ... | @@ -414,24 +434,25 @@ pub const AllErrors = struct { |
| 414 | } | 434 | } |
| 415 | } | 435 | } |
| 416 | for (src.notes) |note| { | 436 | for (src.notes) |note| { |
| 417 | try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan, indent); | 437 | try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent); |
| 418 | } | 438 | } |
| 419 | }, | 439 | }, |
| 420 | .plain => |plain| { | 440 | .plain => |plain| { |
| 421 | ttyconf.setColor(stderr, color); | 441 | ttyconf.setColor(stderr, color); |
| 422 | try stderr.writeByteNTimes(' ', indent); | 442 | try stderr.writeByteNTimes(' ', indent); |
| 423 | try stderr.writeAll(kind); | 443 | try stderr.writeAll(kind); |
| | 444 | try stderr.writeAll(": "); |
| 424 | ttyconf.setColor(stderr, .Reset); | 445 | ttyconf.setColor(stderr, .Reset); |
| 425 | if (plain.count == 1) { | 446 | if (plain.count == 1) { |
| 426 | try stderr.print(" {s}\n", .{plain.msg}); | 447 | try stderr.print("{s}\n", .{plain.msg}); |
| 427 | } else { | 448 | } else { |
| 428 | try stderr.print(" {s}", .{plain.msg}); | 449 | try stderr.print("{s}", .{plain.msg}); |
| 429 | ttyconf.setColor(stderr, .Dim); | 450 | ttyconf.setColor(stderr, .Dim); |
| 430 | try stderr.print(" ({d} times)\n", .{plain.count}); | 451 | try stderr.print(" ({d} times)\n", .{plain.count}); |
| 431 | } | 452 | } |
| 432 | ttyconf.setColor(stderr, .Reset); | 453 | ttyconf.setColor(stderr, .Reset); |
| 433 | for (plain.notes) |note| { | 454 | for (plain.notes) |note| { |
| 434 | try note.renderToStdErrInner(ttyconf, stderr_file, "error:", .Red, indent + 4); | 455 | try note.renderToWriter(ttyconf, stderr, "error", .Red, indent + 4); |
| 435 | } | 456 | } |
| 436 | }, | 457 | }, |
| 437 | } | 458 | } |