Update the terminal library for rendering diagnostics (#7659)

Everything drawn into a buffer was checked against `columns()`. That is
right for wrapping and for line drawing, both of which have somewhere
else to put what doesn't fit, but wrong for `DrawText`, which exists for
text that must not be broken and sometimes has to run past the width
with no other answer available. It and `DrawCodePoint` now check only
that the column is non-negative and the row is one a grid can index, and
widen the buffer as far as the text needs; `DrawWrappedText`,
`DrawHorizontalLine`, `DrawVerticalLine`, and `DrawBox` are unchanged.
That also settles what a caller does after a drawing overhangs, since
`DrawEnd` exists so that a run can continue where the last one ended,
and that continuation was itself a checked error whenever the previous
run overhung. A column computed to be negative, such as a gutter
narrower than the line number it holds, still fails.

A color picked to read against black is hard to read against white, and
nothing in `Capabilities` said which a stream was going into.
`ChooseBackground` reads `COLORFGBG`, which `rxvt` and its derivatives
set to the foreground and background palette indices, and takes anything
it doesn't answer to be dark: guessing dark costs contrast, while
guessing light puts pale text on a pale background. Asking the terminal
itself with an `OSC 11` query is the accurate answer, and needs raw
mode, a timeout, and somewhere to put the reply, so there is a TODO for
it rather than an implementation.

Every corner, tee, and crossing came out of `Charset::Ascii` as `+`,
which left six of the shapes a diagnostic draws indistinguishable: the
rule closing a frame read as the one separating two snippets, and the
anchor opening a diagnostic as the one carrying it on. Each stand-in now
keeps the axis its line runs through, which leaves `+` meaning a
crossing and nothing else. A tee keeps its through-stroke and leaves the
branch to what is drawn beside it, and a corner is `.` where its line
leaves downward and `'` where it arrives from above, which is where
those characters sit in their cells. A box is a box again:

```
    +--+        .--.
    |  |   ->   |  |
    +--+        '--'
```

Assisted-by: Claude Code
This commit is contained in:
Chandler Carruth
2026-08-21 22:19:12 +00:00
committed by GitHub
parent c41033c315
commit 40aa4419c0
6 changed files with 254 additions and 59 deletions
+36 -10
View File
@@ -44,11 +44,38 @@ static constexpr std::array<char32_t, 16> Utf8LineGlyphs = {
U'┼', // left, right, up, down
};
// The ASCII stand-ins, which can only distinguish horizontal, vertical, and
// everything else.
// The ASCII stand-ins. Each keeps the axis its line runs through, which leaves
// `+` meaning a crossing and nothing else:
//
// - Running through horizontally is `-`, vertically `|`, and both ways `+`.
// - A tee keeps its through-stroke and leaves the branch to what is drawn
// beside it: the dashes either side of a `|` are what `├` and `┤` reach, and
// the line under a `-` is what a `┬` reaches. Drawing a tee as `+` reads as
// the crossing it is not.
// - A corner is `.` where its line leaves downward and `'` where it arrives
// from above, which is where those characters sit in their cells.
// - A point, a line between one center and itself, is `.`.
//
// What a diagnostic draws is then still told apart: the rule closing a snippet
// from the one separating two, and the anchor opening a diagnostic from the one
// carrying it on.
static constexpr std::array<char32_t, 16> AsciiLineGlyphs = {
U'+', U'-', U'-', U'-', U'|', U'+', U'+', U'+',
U'|', U'+', U'+', U'+', U'|', U'+', U'+', U'+',
U'.', // (none): a point
U'-', // left
U'-', // right
U'-', // left, right
U'|', // up
U'\'', // left, up
U'\'', // right, up
U'-', // left, right, up
U'|', // down
U'.', // left, down
U'.', // right, down
U'-', // left, right, down
U'|', // up, down
U'|', // left, up, down
U'|', // right, up, down
U'+', // left, right, up, down
};
// Returns the next tab stop after `x` on a line whose stops are `tab_width`
@@ -170,7 +197,7 @@ auto Buffer::AttachCombiningMark(int x, int y, char32_t code_point) -> void {
auto Buffer::DrawCodePoint(int x, int y, char32_t code_point,
const Style& style) -> DrawEnd {
CheckOrigin(x, y);
CheckTextOrigin(x, y);
return {.x = PlaceCodePoint(x, y, code_point, style), .y = y};
}
@@ -299,11 +326,10 @@ template <typename PlaceFn>
auto Buffer::WalkText(int x, int y, int margin, llvm::StringRef text,
PlaceFn place) const -> DrawEnd {
CheckTextSize(text);
CARBON_CHECK(
margin >= 0 && margin <= x && x < columns_ && y >= 0 && y < MaxRows,
"Text at ({0}, {1}) with a margin of {2} is outside the {3} "
"columns and {4} rows a buffer covers, or left of its margin.",
x, y, margin, columns_, MaxRows);
CARBON_CHECK(margin >= 0 && margin <= x && y >= 0 && y < MaxRows,
"Text at ({0}, {1}) with a margin of {2} is outside the {3} "
"rows a buffer covers, or left of its margin.",
x, y, margin, MaxRows);
int cur_x = x;
int cur_y = y;