From 4c0b7807f80dc9eb943c8216cb3170cc9272a6d0 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 27 Apr 2021 18:01:28 +0000 Subject: [PATCH] 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 Auto-Submit: Ben Clayton Kokoro: Kokoro Reviewed-by: Antonio Maiorano --- src/diagnostic/formatter.cc | 65 +++++++++++++++--------- src/diagnostic/formatter.h | 2 + src/diagnostic/formatter_test.cc | 87 ++++++++++++++++++++++---------- 3 files changed, 105 insertions(+), 49 deletions(-) diff --git a/src/diagnostic/formatter.cc b/src/diagnostic/formatter.cc index 6f5a9ecc1f..7461ba9529 100644 --- a/src/diagnostic/formatter.cc +++ b/src/diagnostic/formatter.cc @@ -211,33 +211,52 @@ void Formatter::format(const Diagnostic& diag, State& state) const { state.newline(); state.set_style({Color::kDefault, false}); - for (size_t line = rng.begin.line; line <= rng.end.line; line++) { - if (line < src.file_content->lines.size() + 1) { - auto len = src.file_content->lines[line - 1].size(); + for (size_t line_num = rng.begin.line; + (line_num <= rng.end.line) && (src.file_content->lines.size() + 1); + line_num++) { + auto& line = src.file_content->lines[line_num - 1]; + auto line_len = line.size(); - state << src.file_content->lines[line - 1]; - - state.newline(); - 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(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); + for (auto c : line) { + if (c == '\t') { + state.repeat(' ', style_.tab_width); } else { - // Middle of multi-line - state.repeat('^', len); + state << c; } - 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( + 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({}); diff --git a/src/diagnostic/formatter.h b/src/diagnostic/formatter.h index 7e037cb9be..dbb28564a2 100644 --- a/src/diagnostic/formatter.h +++ b/src/diagnostic/formatter.h @@ -37,6 +37,8 @@ class Formatter { bool print_line = true; /// print a newline at the end of a diagnostic list bool print_newline_at_end = true; + /// width of a tab character + size_t tab_width = 2u; }; /// Constructor for the formatter using a default style. diff --git a/src/diagnostic/formatter_test.cc b/src/diagnostic/formatter_test.cc index 0863030af4..8495dc965d 100644 --- a/src/diagnostic/formatter_test.cc +++ b/src/diagnostic/formatter_test.cc @@ -21,11 +21,11 @@ namespace tint { namespace diag { namespace { -constexpr const char* content = - R"(the cat says meow -the dog says woof -the snake says quack -the snail says ??? +constexpr const char* content = // Note: words are tab-delimited + R"(the cat says meow +the dog says woof +the snake says quack +the snail says ??? )"; class DiagFormatterTest : public testing::Test { @@ -96,16 +96,16 @@ TEST_F(DiagFormatterTest, WithLine) { Formatter fmt{{false, false, true, false}}; auto got = fmt.format(List{diag_note, diag_warn, diag_err}); auto* expect = R"(1:14: purr -the cat says meow - ^ +the cat says meow + ^ 2:14: grrr -the dog says woof - ^^^^ +the dog says woof + ^^^^ 3:16 abc123: hiss -the snake says quack - ^^^^^ +the snake says quack + ^^^^^ )"; ASSERT_EQ(expect, got); } @@ -114,16 +114,16 @@ TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) { Formatter fmt{{true, true, true, false}}; auto got = fmt.format(List{diag_note, diag_warn, diag_err}); auto* expect = R"(file.name:1:14 note: purr -the cat says meow - ^ +the cat says meow + ^ file.name:2:14 warning: grrr -the dog says woof - ^^^^ +the dog says woof + ^^^^ file.name:3:16 error abc123: hiss -the snake says quack - ^^^^^ +the snake says quack + ^^^^^ )"; ASSERT_EQ(expect, got); } @@ -135,12 +135,47 @@ TEST_F(DiagFormatterTest, BasicWithMultiLine) { Formatter fmt{{false, false, true, false}}; auto got = fmt.format(List{multiline}); auto* expect = R"(2:9: multiline -the dog says woof - ^^^^^^^^^ -the snake says quack +the dog says woof + ^^^^^^^^^^ +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); } @@ -149,8 +184,8 @@ TEST_F(DiagFormatterTest, ICE) { Formatter fmt{{}}; auto got = fmt.format(List{diag_ice}); 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. * @@ -167,8 +202,8 @@ TEST_F(DiagFormatterTest, Fatal) { Formatter fmt{{}}; auto got = fmt.format(List{diag_fatal}); auto* expect = R"(file.name:4:16 fatal: nothing -the snail says ??? - ^^^ +the snail says ??? + ^^^ ******************************************************************** * The tint shader compiler has encountered an unexpected error. *