| ... | @@ -610,8 +610,8 @@ const Prefix = enum { | ... | @@ -610,8 +610,8 @@ const Prefix = enum { |
| 610 | | 610 | |
| 611 | fn symbol(prefix: Prefix, ascii: bool) []const u8 { | 611 | fn symbol(prefix: Prefix, ascii: bool) []const u8 { |
| 612 | return switch (prefix) { | 612 | return switch (prefix) { |
| 613 | .@"error" => if (ascii) " X " else " ✘ ", | 613 | .@"error" => if (ascii) "X" else "✘", |
| 614 | .warn => if (ascii) " ! " else " ▲ ", | 614 | .warn => if (ascii) "!" else "▲", |
| 615 | .note => "", | 615 | .note => "", |
| 616 | }; | 616 | }; |
| 617 | } | 617 | } |
| ... | @@ -676,6 +676,17 @@ pub fn formatItem( | ... | @@ -676,6 +676,17 @@ pub fn formatItem( |
| 676 | | 676 | |
| 677 | assert(ctx.components.items.len > 0); | 677 | assert(ctx.components.items.len > 0); |
| 678 | | 678 | |
| | 679 | const width = brk: { |
| | 680 | var max: usize = 0; |
| | 681 | for (ctx.components.items) |component| { |
| | 682 | if (component.location) |loc| { |
| | 683 | max = @max(loc.loc.line.oneBased(), max); |
| | 684 | } |
| | 685 | } |
| | 686 | |
| | 687 | break :brk if (max > 0) std.fmt.count("{d}", .{max}) else 0; |
| | 688 | }; |
| | 689 | |
| 679 | for (ctx.components.items, 1..) |component, i| { | 690 | for (ctx.components.items, 1..) |component, i| { |
| 680 | const prefix: Prefix = if (i == 1) | 691 | const prefix: Prefix = if (i == 1) |
| 681 | switch (item_type) { | 692 | switch (item_type) { |
| ... | @@ -686,15 +697,18 @@ pub fn formatItem( | ... | @@ -686,15 +697,18 @@ pub fn formatItem( |
| 686 | | 697 | |
| 687 | const is_last = i == ctx.components.items.len; | 698 | const is_last = i == ctx.components.items.len; |
| 688 | | 699 | |
| | 700 | try writer.writeByteNTimes(' ', width); |
| | 701 | |
| 689 | if (i != 1) { | 702 | if (i != 1) { |
| 690 | try tty.setColor(writer, .dim); | 703 | try tty.setColor(writer, .dim); |
| 691 | try writer.writeAll(" │ "); | 704 | try writer.writeAll("│"); |
| 692 | try tty.setColor(writer, .reset); | 705 | try tty.setColor(writer, .reset); |
| 693 | } | 706 | } |
| 694 | | 707 | |
| 695 | try tty.setColor(writer, prefix.color()); | 708 | try tty.setColor(writer, prefix.color()); |
| 696 | try tty.setColor(writer, .bold); | 709 | try tty.setColor(writer, .bold); |
| 697 | try writer.writeAll(prefix.symbol(false)); | 710 | try writer.writeAll(prefix.symbol(false)); |
| | 711 | try writer.writeAll(" "); |
| 698 | try writer.writeAll(@tagName(prefix)); | 712 | try writer.writeAll(@tagName(prefix)); |
| 699 | try tty.setColor(writer, .reset); | 713 | try tty.setColor(writer, .reset); |
| 700 | try tty.setColor(writer, .dim); | 714 | try tty.setColor(writer, .dim); |
| ... | @@ -712,7 +726,8 @@ pub fn formatItem( | ... | @@ -712,7 +726,8 @@ pub fn formatItem( |
| 712 | const at = component.location orelse { | 726 | const at = component.location orelse { |
| 713 | if (is_last) { | 727 | if (is_last) { |
| 714 | try tty.setColor(writer, .dim); | 728 | try tty.setColor(writer, .dim); |
| 715 | try writer.print(" {s}", .{"┴"}); | 729 | try writer.writeByteNTimes(' ', width); |
| | 730 | try writer.print("{s}", .{"┴"}); |
| 716 | try writer.writeAll("\n"); | 731 | try writer.writeAll("\n"); |
| 717 | try tty.setColor(writer, .reset); | 732 | try tty.setColor(writer, .reset); |
| 718 | } | 733 | } |
| ... | @@ -721,8 +736,13 @@ pub fn formatItem( | ... | @@ -721,8 +736,13 @@ pub fn formatItem( |
| 721 | | 736 | |
| 722 | const line_before = utf8_source[at.loc.line_start..at.byte]; | 737 | const line_before = utf8_source[at.loc.line_start..at.byte]; |
| 723 | const trimmed_before = std.mem.trimLeft(u8, line_before, " \t\r"); | 738 | const trimmed_before = std.mem.trimLeft(u8, line_before, " \t\r"); |
| 724 | const line_after = utf8_source[at.byte + at.slice.len .. at.loc.line_end + 1]; | 739 | // The text after the highlighted component can be out of bounds if the |
| 725 | const width = std.fmt.count("{d}", .{at.loc.line.oneBased()}); | 740 | // highlighted token is EOF, which is hardcoded to " " to highlight |
| | 741 | // empty space after the line. |
| | 742 | const line_after = if (at.byte + at.slice.len < at.loc.line_end) |
| | 743 | utf8_source[at.byte + at.slice.len .. at.loc.line_end] |
| | 744 | else |
| | 745 | ""; |
| 726 | | 746 | |
| 727 | try tty.setColor(writer, .dim); | 747 | try tty.setColor(writer, .dim); |
| 728 | try writer.writeByteNTimes(' ', width); | 748 | try writer.writeByteNTimes(' ', width); |
| ... | @@ -741,12 +761,13 @@ pub fn formatItem( | ... | @@ -741,12 +761,13 @@ pub fn formatItem( |
| 741 | try writer.writeAll(at.slice); | 761 | try writer.writeAll(at.slice); |
| 742 | try tty.setColor(writer, .reset); | 762 | try tty.setColor(writer, .reset); |
| 743 | try writer.writeAll(line_after); | 763 | try writer.writeAll(line_after); |
| | 764 | try writer.writeAll("\n"); |
| 744 | try tty.setColor(writer, .dim); | 765 | try tty.setColor(writer, .dim); |
| 745 | try writer.writeAll(if (is_last) " ╰" else " ├"); | 766 | try writer.writeByteNTimes(' ', width); |
| | 767 | try writer.writeAll(if (is_last) "╰" else "├"); |
| 746 | try writer.writeBytesNTimes( | 768 | try writer.writeBytesNTimes( |
| 747 | "─", | 769 | "─", |
| 748 | width + | 770 | 1 + (at.byte - at.loc.line_start) - |
| 749 | (at.byte - at.loc.line_start) - | | |
| 750 | (line_before.len - trimmed_before.len), | 771 | (line_before.len - trimmed_before.len), |
| 751 | ); | 772 | ); |
| 752 | try tty.setColor(writer, .reset); | 773 | try tty.setColor(writer, .reset); |
| ... | @@ -912,7 +933,10 @@ pub const Highlight = union(enum) { | ... | @@ -912,7 +933,10 @@ pub const Highlight = union(enum) { |
| 912 | | 933 | |
| 913 | break :loc .{ | 934 | break :loc .{ |
| 914 | .loc = location, | 935 | .loc = location, |
| 915 | .slice = if (token.tag != .eof) token.slice(ast.utf8_source) else "\n", | 936 | .slice = if (token.tag != .eof) |
| | 937 | token.slice(ast.utf8_source) |
| | 938 | else |
| | 939 | " ", |
| 916 | .byte = token.offset, | 940 | .byte = token.offset, |
| 917 | }; | 941 | }; |
| 918 | }, | 942 | }, |