formatter: handle tabs

Transform these into whitespace so we can sensibly align the ^^ markers
with the text.

Change-Id: I151c0e55bc0a02c1cff6e381cb9839c9f9abdaf6
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48694
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Ben Clayton 2021-04-27 18:01:28 +00:00 committed by Commit Bot service account
parent 3fe2779402
commit 4c0b7807f8
3 changed files with 105 additions and 49 deletions

View File

@ -211,33 +211,52 @@ void Formatter::format(const Diagnostic& diag, State& state) const {
state.newline(); state.newline();
state.set_style({Color::kDefault, false}); state.set_style({Color::kDefault, false});
for (size_t line = rng.begin.line; line <= rng.end.line; line++) { for (size_t line_num = rng.begin.line;
if (line < src.file_content->lines.size() + 1) { (line_num <= rng.end.line) && (src.file_content->lines.size() + 1);
auto len = src.file_content->lines[line - 1].size(); line_num++) {
auto& line = src.file_content->lines[line_num - 1];
auto line_len = line.size();
state << src.file_content->lines[line - 1]; for (auto c : line) {
if (c == '\t') {
state.newline(); state.repeat(' ', style_.tab_width);
state.set_style({Color::kCyan, false});
if (line == rng.begin.line && line == rng.end.line) {
// Single line
state.repeat(' ', rng.begin.column - 1);
state.repeat('^',
std::max<size_t>(rng.end.column - rng.begin.column, 1));
} else if (line == rng.begin.line) {
// Start of multi-line
state.repeat(' ', rng.begin.column - 1);
state.repeat('^', len - (rng.begin.column - 1));
} else if (line == rng.end.line) {
// End of multi-line
state.repeat('^', rng.end.column - 1);
} else { } else {
// Middle of multi-line state << c;
state.repeat('^', len);
} }
state.newline();
} }
state.newline();
state.set_style({Color::kCyan, false});
// Count the number of glyphs in the line span.
// start and end use 1-based indexing .
auto num_glyphs = [&](size_t start, size_t end) {
size_t count = 0;
start = (start > 0) ? (start - 1) : 0;
end = (end > 0) ? (end - 1) : 0;
for (size_t i = start; (i < end) && (i < line_len); i++) {
count += (line[i] == '\t') ? style_.tab_width : 1;
}
return count;
};
if (line_num == rng.begin.line && line_num == rng.end.line) {
// Single line
state.repeat(' ', num_glyphs(1, rng.begin.column));
state.repeat('^', std::max<size_t>(
num_glyphs(rng.begin.column, rng.end.column), 1));
} else if (line_num == rng.begin.line) {
// Start of multi-line
state.repeat(' ', num_glyphs(1, rng.begin.column));
state.repeat('^', num_glyphs(rng.begin.column, line_len + 1));
} else if (line_num == rng.end.line) {
// End of multi-line
state.repeat('^', num_glyphs(1, rng.end.column));
} else {
// Middle of multi-line
state.repeat('^', num_glyphs(1, line_len + 1));
}
state.newline();
} }
state.set_style({}); state.set_style({});

View File

@ -37,6 +37,8 @@ class Formatter {
bool print_line = true; bool print_line = true;
/// print a newline at the end of a diagnostic list /// print a newline at the end of a diagnostic list
bool print_newline_at_end = true; bool print_newline_at_end = true;
/// width of a tab character
size_t tab_width = 2u;
}; };
/// Constructor for the formatter using a default style. /// Constructor for the formatter using a default style.

View File

@ -21,11 +21,11 @@ namespace tint {
namespace diag { namespace diag {
namespace { namespace {
constexpr const char* content = constexpr const char* content = // Note: words are tab-delimited
R"(the cat says meow R"(the cat says meow
the dog says woof the dog says woof
the snake says quack the snake says quack
the snail says ??? the snail says ???
)"; )";
class DiagFormatterTest : public testing::Test { class DiagFormatterTest : public testing::Test {
@ -96,16 +96,16 @@ TEST_F(DiagFormatterTest, WithLine) {
Formatter fmt{{false, false, true, false}}; Formatter fmt{{false, false, true, false}};
auto got = fmt.format(List{diag_note, diag_warn, diag_err}); auto got = fmt.format(List{diag_note, diag_warn, diag_err});
auto* expect = R"(1:14: purr auto* expect = R"(1:14: purr
the cat says meow the cat says meow
^ ^
2:14: grrr 2:14: grrr
the dog says woof the dog says woof
^^^^ ^^^^
3:16 abc123: hiss 3:16 abc123: hiss
the snake says quack the snake says quack
^^^^^ ^^^^^
)"; )";
ASSERT_EQ(expect, got); ASSERT_EQ(expect, got);
} }
@ -114,16 +114,16 @@ TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
Formatter fmt{{true, true, true, false}}; Formatter fmt{{true, true, true, false}};
auto got = fmt.format(List{diag_note, diag_warn, diag_err}); auto got = fmt.format(List{diag_note, diag_warn, diag_err});
auto* expect = R"(file.name:1:14 note: purr auto* expect = R"(file.name:1:14 note: purr
the cat says meow the cat says meow
^ ^
file.name:2:14 warning: grrr file.name:2:14 warning: grrr
the dog says woof the dog says woof
^^^^ ^^^^
file.name:3:16 error abc123: hiss file.name:3:16 error abc123: hiss
the snake says quack the snake says quack
^^^^^ ^^^^^
)"; )";
ASSERT_EQ(expect, got); ASSERT_EQ(expect, got);
} }
@ -135,12 +135,47 @@ TEST_F(DiagFormatterTest, BasicWithMultiLine) {
Formatter fmt{{false, false, true, false}}; Formatter fmt{{false, false, true, false}};
auto got = fmt.format(List{multiline}); auto got = fmt.format(List{multiline});
auto* expect = R"(2:9: multiline auto* expect = R"(2:9: multiline
the dog says woof the dog says woof
^^^^^^^^^ ^^^^^^^^^^
the snake says quack the snake says quack
^^^^^^^^^^^^^^^^^^^^^^^
the snail says ???
^^^^^^^^^^^^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, BasicWithFileSeverityLineTab4) {
Formatter fmt{{true, true, true, false, 4u}};
auto got = fmt.format(List{diag_note, diag_warn, diag_err});
auto* expect = R"(file.name:1:14 note: purr
the cat says meow
^
file.name:2:14 warning: grrr
the dog says woof
^^^^
file.name:3:16 error abc123: hiss
the snake says quack
^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, BasicWithMultiLineTab4) {
Diagnostic multiline{Severity::Warning,
Source{Source::Range{{2, 9}, {4, 15}}, &file},
"multiline"};
Formatter fmt{{false, false, true, false, 4u}};
auto got = fmt.format(List{multiline});
auto* expect = R"(2:9: multiline
the dog says woof
^^^^^^^^^^^^
the snake says quack
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
the snail says ???
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
the snail says ???
^^^^^^^^^^^^^^
)"; )";
ASSERT_EQ(expect, got); ASSERT_EQ(expect, got);
} }
@ -149,8 +184,8 @@ TEST_F(DiagFormatterTest, ICE) {
Formatter fmt{{}}; Formatter fmt{{}};
auto got = fmt.format(List{diag_ice}); auto got = fmt.format(List{diag_ice});
auto* expect = R"(file.name:4:16 internal compiler error: unreachable auto* expect = R"(file.name:4:16 internal compiler error: unreachable
the snail says ??? the snail says ???
^^^ ^^^
******************************************************************** ********************************************************************
* The tint shader compiler has encountered an unexpected error. * * The tint shader compiler has encountered an unexpected error. *
@ -167,8 +202,8 @@ TEST_F(DiagFormatterTest, Fatal) {
Formatter fmt{{}}; Formatter fmt{{}};
auto got = fmt.format(List{diag_fatal}); auto got = fmt.format(List{diag_fatal});
auto* expect = R"(file.name:4:16 fatal: nothing auto* expect = R"(file.name:4:16 fatal: nothing
the snail says ??? the snail says ???
^^^ ^^^
******************************************************************** ********************************************************************
* The tint shader compiler has encountered an unexpected error. * * The tint shader compiler has encountered an unexpected error. *