diff --git a/experimental/parser/testdata/lexer/braces/interleaved.proto.stderr.txt b/experimental/parser/testdata/lexer/braces/interleaved.proto.stderr.txt index b8913465..7682d987 100644 --- a/experimental/parser/testdata/lexer/braces/interleaved.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/braces/interleaved.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `[` delimiter --> testdata/lexer/braces/interleaved.proto:2:5 - | + | 2 | [ | ^ expected a closing `]` 3 | } diff --git a/experimental/parser/testdata/lexer/braces/stray-close.proto.stderr.txt b/experimental/parser/testdata/lexer/braces/stray-close.proto.stderr.txt index 15c093c8..60c77bdc 100644 --- a/experimental/parser/testdata/lexer/braces/stray-close.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/braces/stray-close.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `{` delimiter --> testdata/lexer/braces/stray-close.proto:1:1 - | + | 1 | { | ^ expected a closing `}` 2 | ] diff --git a/experimental/parser/testdata/lexer/braces/stray-open.proto.stderr.txt b/experimental/parser/testdata/lexer/braces/stray-open.proto.stderr.txt index 39147f8c..142180a6 100644 --- a/experimental/parser/testdata/lexer/braces/stray-open.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/braces/stray-open.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `[` delimiter --> testdata/lexer/braces/stray-open.proto:2:5 - | + | 2 | [ | ^ expected a closing `]` diff --git a/experimental/parser/testdata/lexer/braces/unclosed.proto.stderr.txt b/experimental/parser/testdata/lexer/braces/unclosed.proto.stderr.txt index d2d68bcb..b8a1f5af 100644 --- a/experimental/parser/testdata/lexer/braces/unclosed.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/braces/unclosed.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `(` delimiter --> testdata/lexer/braces/unclosed.proto:1:1 - | + | 1 | ( | ^ expected a closing `)` diff --git a/experimental/parser/testdata/lexer/braces/unopened.proto.stderr.txt b/experimental/parser/testdata/lexer/braces/unopened.proto.stderr.txt index e6b21025..9839c86a 100644 --- a/experimental/parser/testdata/lexer/braces/unopened.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/braces/unopened.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `)` delimiter --> testdata/lexer/braces/unopened.proto:1:1 - | + | 1 | ) | ^ expected a closing `(` diff --git a/experimental/parser/testdata/lexer/comments/nested.proto.stderr.txt b/experimental/parser/testdata/lexer/comments/nested.proto.stderr.txt index fcc9a94c..f5468bc7 100644 --- a/experimental/parser/testdata/lexer/comments/nested.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/comments/nested.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `*/` delimiter --> testdata/lexer/comments/nested.proto:4:1 - | + | 4 | */ | ^^ expected a closing `/*` = note: Protobuf does not support nested block comments diff --git a/experimental/parser/testdata/lexer/comments/unterminated.proto.stderr.txt b/experimental/parser/testdata/lexer/comments/unterminated.proto.stderr.txt index e5669d23..df4fcb03 100644 --- a/experimental/parser/testdata/lexer/comments/unterminated.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/comments/unterminated.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered unmatched `/*` delimiter --> testdata/lexer/comments/unterminated.proto:1:1 - | + | 1 | /* | ^^ expected a closing `*/` diff --git a/experimental/parser/testdata/lexer/idents/non-ascii.proto.stderr.txt b/experimental/parser/testdata/lexer/idents/non-ascii.proto.stderr.txt index 9af1f78a..fe04551e 100644 --- a/experimental/parser/testdata/lexer/idents/non-ascii.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/idents/non-ascii.proto.stderr.txt @@ -1,31 +1,31 @@ error: unrecognized token --> testdata/lexer/idents/non-ascii.proto:1:7 - | + | 1 | kitty_🐈⬛ - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: non-ASCII identifiers are not allowed --> testdata/lexer/idents/non-ascii.proto:2:1 - | - 2 | 黑猫 - | ^^^^ + | + 2 | 黑猫 + | ^^^^ error: non-ASCII identifiers are not allowed --> testdata/lexer/idents/non-ascii.proto:3:1 - | + | 3 | काली बिल्ली - | ^^^^ + | ^^^^ error: non-ASCII identifiers are not allowed --> testdata/lexer/idents/non-ascii.proto:3:6 - | + | 3 | काली बिल्ली - | ^^^^^ + | ^^^^^ error: non-ASCII identifiers are not allowed --> testdata/lexer/idents/non-ascii.proto:4:1 - | + | 4 | 黑猫_suffix - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ encountered 5 errors diff --git a/experimental/parser/testdata/lexer/numbers/bad-digit.proto.stderr.txt b/experimental/parser/testdata/lexer/numbers/bad-digit.proto.stderr.txt index 8ebee048..c5b43e81 100644 --- a/experimental/parser/testdata/lexer/numbers/bad-digit.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/numbers/bad-digit.proto.stderr.txt @@ -1,31 +1,31 @@ error: unexpected characters in integer literal --> testdata/lexer/numbers/bad-digit.proto:1:1 - | + | 1 | 0f - | ^^ + | ^^ error: unexpected characters in integer literal --> testdata/lexer/numbers/bad-digit.proto:2:1 - | + | 2 | 0b02 - | ^^^^ + | ^^^^ error: unexpected characters in integer literal --> testdata/lexer/numbers/bad-digit.proto:3:1 - | + | 3 | 0o08 - | ^^^^ + | ^^^^ error: unexpected characters in integer literal --> testdata/lexer/numbers/bad-digit.proto:4:1 - | + | 4 | 08 - | ^^ + | ^^ error: unexpected characters in integer literal --> testdata/lexer/numbers/bad-digit.proto:5:1 - | + | 5 | 0x0G - | ^^^^ + | ^^^^ encountered 5 errors diff --git a/experimental/parser/testdata/lexer/numbers/exotic-base.proto.stderr.txt b/experimental/parser/testdata/lexer/numbers/exotic-base.proto.stderr.txt index 46bd2aa8..72c05e64 100644 --- a/experimental/parser/testdata/lexer/numbers/exotic-base.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/numbers/exotic-base.proto.stderr.txt @@ -1,49 +1,49 @@ error: unsupported base for integer literal --> testdata/lexer/numbers/exotic-base.proto:1:1 help: use a hexadecimal literal instead - | + | 1 | - 0b1100101 1 | + 0x65 - | + | = note: Protobuf does not support binary literals error: unsupported base for integer literal --> testdata/lexer/numbers/exotic-base.proto:2:1 help: use a hexadecimal literal instead - | + | 2 | - 0B1010101 2 | + 0x55 - | + | = note: Protobuf does not support binary literals error: unsupported base for integer literal --> testdata/lexer/numbers/exotic-base.proto:3:1 help: remove the `o` - | + | 3 | - 0o1234567 3 | + 01234567 - | + | = note: octal literals are prefixed with `0`, not `0o` error: unsupported base for integer literal --> testdata/lexer/numbers/exotic-base.proto:4:1 help: remove the `o` - | + | 4 | - 0O1234567 4 | + 01234567 - | + | = note: octal literals are prefixed with `0`, not `0o` error: unexpected characters in floating-point literal --> testdata/lexer/numbers/exotic-base.proto:6:1 - | + | 6 | 0x10203.4 - | ^^^^^^^^^ + | ^^^^^^^^^ error: unexpected characters in floating-point literal --> testdata/lexer/numbers/exotic-base.proto:7:1 - | + | 7 | 0X12.ffP10 - | ^^^^^^^^^^ + | ^^^^^^^^^^ encountered 6 errors diff --git a/experimental/parser/testdata/lexer/numbers/thousands.proto.stderr.txt b/experimental/parser/testdata/lexer/numbers/thousands.proto.stderr.txt index 67c1d49f..cfcf4f75 100644 --- a/experimental/parser/testdata/lexer/numbers/thousands.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/numbers/thousands.proto.stderr.txt @@ -1,55 +1,55 @@ error: integer literal contains underscores --> testdata/lexer/numbers/thousands.proto:1:1 help: remove these underscores - | + | 1 | - 1_000_000 1 | + 1000000 - | + | = note: Protobuf does not support Go/Java/Rust-style thousands separators error: unsupported base for integer literal --> testdata/lexer/numbers/thousands.proto:2:1 help: use a hexadecimal literal instead - | + | 2 | - 0b1_000_000 2 | + 0x40 - | + | = note: Protobuf does not support binary literals error: unsupported base for integer literal --> testdata/lexer/numbers/thousands.proto:3:1 help: remove the `o` - | + | 3 | - 0o1_000_000 3 | + 01_000_000 - | + | = note: octal literals are prefixed with `0`, not `0o` error: integer literal contains underscores --> testdata/lexer/numbers/thousands.proto:4:1 help: remove these underscores - | + | 4 | - 0x1_000_000 4 | + 0x1000000 - | + | = note: Protobuf does not support Go/Java/Rust-style thousands separators error: integer literal contains underscores --> testdata/lexer/numbers/thousands.proto:5:1 help: remove these underscores - | + | 5 | - 01_000_000 5 | + 01000000 - | + | = note: Protobuf does not support Go/Java/Rust-style thousands separators error: floating-point literal contains underscores --> testdata/lexer/numbers/thousands.proto:6:1 help: remove these underscores - | + | 6 | - 1_000_000.00 6 | + 1000000.00 - | + | = note: Protobuf does not support Go/Java/Rust-style thousands separators encountered 6 errors diff --git a/experimental/parser/testdata/lexer/punct.proto.stderr.txt b/experimental/parser/testdata/lexer/punct.proto.stderr.txt index febc267a..f2a18349 100644 --- a/experimental/parser/testdata/lexer/punct.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/punct.proto.stderr.txt @@ -1,55 +1,55 @@ error: unrecognized token --> testdata/lexer/punct.proto:2:1 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:3 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:5 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:7 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:9 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:11 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:13 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:15 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ error: unrecognized token --> testdata/lexer/punct.proto:2:17 - | + | 2 | ! @ # $ % ^ & * + _ - | ^ + | ^ encountered 9 errors diff --git a/experimental/parser/testdata/lexer/strings/bad-contents.proto.stderr.txt b/experimental/parser/testdata/lexer/strings/bad-contents.proto.stderr.txt index 97d8f816..7ff7563f 100644 --- a/experimental/parser/testdata/lexer/strings/bad-contents.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/strings/bad-contents.proto.stderr.txt @@ -1,42 +1,42 @@ error: unescaped newlines are not permitted in string literals --> testdata/lexer/strings/bad-contents.proto:1:13 - | + | 1 | "this string | ^ replace this with `\n` error: unescaped newlines are not permitted in string literals --> testdata/lexer/strings/bad-contents.proto:3:2 - | + | 3 | " | ^ replace this with `\n` error: unescaped NUL bytes are not permitted in string literals --> testdata/lexer/strings/bad-contents.proto:6:30 - | + | 6 | "this string contains a nul: " | ^^^^^^^^ replace this with `\0` or `\x00` error: unescaped NUL bytes are not permitted in string literals --> testdata/lexer/strings/bad-contents.proto:7:29 - | + | 7 | "this string contains a nul in the middle" | ^^^^^^^^ replace this with `\0` or `\x00` error: unescaped NUL bytes are not permitted in string literals --> testdata/lexer/strings/bad-contents.proto:8:2 - | + | 8 | "" | ^^^^^^^^ replace this with `\0` or `\x00` warning: non-printable character in string literal --> testdata/lexer/strings/bad-contents.proto:10:57 - | + | 10 | "this string contains some other non-graphic character: " | ^^^^^^^^ help: consider escaping this with e.g. `\x01` instead warning: non-printable character in string literal --> testdata/lexer/strings/bad-contents.proto:11:39 - | + | 11 | "this is graphic but non-ASCII space: " | ^^^^^^^^ help: consider escaping this with e.g. `\u00a0` instead diff --git a/experimental/parser/testdata/lexer/strings/bad-escape.proto.stderr.txt b/experimental/parser/testdata/lexer/strings/bad-escape.proto.stderr.txt index 79adb7a9..4ca2b66b 100644 --- a/experimental/parser/testdata/lexer/strings/bad-escape.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/strings/bad-escape.proto.stderr.txt @@ -1,42 +1,42 @@ error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:1:2 - | + | 1 | "\k" "\x" "\9" "\u", "\U" - | ^^ + | ^^ error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:1:7 - | + | 1 | "\k" "\x" "\9" "\u", "\U" | ^^ `\x` must be followed by at least one hex digit error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:1:12 - | + | 1 | "\k" "\x" "\9" "\u", "\U" - | ^^ + | ^^ error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:1:17 - | + | 1 | "\k" "\x" "\9" "\u", "\U" | ^^ `\u` must be followed by exactly 4 hex digits error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:1:23 - | + | 1 | "\k" "\x" "\9" "\u", "\U" | ^^ `\U` must be followed by exactly 8 hex digits error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:2:2 - | + | 2 | "\u000" "\U0000000" | ^^^^^ `\u` must be followed by exactly 4 hex digits error: invalid escape sequence --> testdata/lexer/strings/bad-escape.proto:2:10 - | + | 2 | "\u000" "\U0000000" | ^^^^^^^^^ `\U` must be followed by exactly 8 hex digits diff --git a/experimental/parser/testdata/lexer/strings/unclosed1.proto.stderr.txt b/experimental/parser/testdata/lexer/strings/unclosed1.proto.stderr.txt index a0abb418..41a352cc 100644 --- a/experimental/parser/testdata/lexer/strings/unclosed1.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/strings/unclosed1.proto.stderr.txt @@ -1,6 +1,6 @@ error: unterminated string literal --> testdata/lexer/strings/unclosed1.proto:1:3 - | + | 1 | """ | ^ expected to be terminated by `"` = note: this string consists of a single orphaned quote diff --git a/experimental/parser/testdata/lexer/strings/unclosed2.proto.stderr.txt b/experimental/parser/testdata/lexer/strings/unclosed2.proto.stderr.txt index 286349a4..0f05af55 100644 --- a/experimental/parser/testdata/lexer/strings/unclosed2.proto.stderr.txt +++ b/experimental/parser/testdata/lexer/strings/unclosed2.proto.stderr.txt @@ -1,6 +1,6 @@ error: unterminated string literal --> testdata/lexer/strings/unclosed2.proto:1:1 - | + | 1 | '\' | ^^^ expected to be terminated by `'` = note: this string appears to end in an escaped quote; replace `\'` with `\\''` diff --git a/experimental/parser/testdata/parser/def/ordering.proto.stderr.txt b/experimental/parser/testdata/parser/def/ordering.proto.stderr.txt index a9dcddcd..c1ccc797 100644 --- a/experimental/parser/testdata/parser/def/ordering.proto.stderr.txt +++ b/experimental/parser/testdata/parser/def/ordering.proto.stderr.txt @@ -1,6 +1,6 @@ error: encountered more than one method parameter list --> testdata/parser/def/ordering.proto:20:13 - | + | 20 | M x (T) (T); | --- ^^^ help: consider removing this | | @@ -8,37 +8,37 @@ error: encountered more than one method parameter list error: unexpected method parameter list after method return type --> testdata/parser/def/ordering.proto:21:21 - | + | 21 | M x returns (T) (T); - | ----------- ^^^ + | ----------- ^^^ | | | previous method return type is here error: missing `(...)` around method return type --> testdata/parser/def/ordering.proto:22:17 - | + | 22 | M x returns T (T); | ^ help: replace this with `(T)` error: unexpected method parameter list after method return type --> testdata/parser/def/ordering.proto:22:19 - | + | 22 | M x returns T (T); - | --------- ^^^ + | --------- ^^^ | | | previous method return type is here error: unexpected method parameter list after compact options --> testdata/parser/def/ordering.proto:23:21 - | + | 23 | M x [foo = bar] (T); - | ----------- ^^^ + | ----------- ^^^ | | | previous compact options is here error: encountered more than one method return type --> testdata/parser/def/ordering.proto:26:21 - | + | 26 | M x returns (T) returns (T); | ----------- ^^^^^^^^^^^ help: consider removing this | | @@ -46,21 +46,21 @@ error: encountered more than one method return type error: unexpected method return type after compact options --> testdata/parser/def/ordering.proto:27:21 - | + | 27 | M x [foo = bar] returns (T); - | ----------- ^^^^^^^^^^^ + | ----------- ^^^^^^^^^^^ | | | previous compact options is here error: missing `(...)` around method return type --> testdata/parser/def/ordering.proto:30:17 - | + | 30 | M x returns T returns T; | ^ help: replace this with `(T)` error: encountered more than one method return type --> testdata/parser/def/ordering.proto:30:19 - | + | 30 | M x returns T returns T; | --------- ^^^^^^^^^ help: consider removing this | | @@ -68,35 +68,35 @@ error: encountered more than one method return type error: missing `(...)` around method return type --> testdata/parser/def/ordering.proto:31:17 - | + | 31 | M x returns T [] returns T; | ^ help: replace this with `(T)` error: unexpected method return type after compact options --> testdata/parser/def/ordering.proto:31:22 - | + | 31 | M x returns T [] returns T; - | -- ^^^^^^^^^ + | -- ^^^^^^^^^ | | | previous compact options is here error: unexpected method return type after compact options --> testdata/parser/def/ordering.proto:32:21 - | + | 32 | M x [foo = bar] returns T; - | ----------- ^^^^^^^^^ + | ----------- ^^^^^^^^^ | | | previous compact options is here error: missing `(...)` around method return type --> testdata/parser/def/ordering.proto:32:29 - | + | 32 | M x [foo = bar] returns T; | ^ help: replace this with `(T)` error: encountered more than one message field tag --> testdata/parser/def/ordering.proto:35:13 - | + | 35 | M x = 1 = 1; | --- ^^^ help: consider removing this | | @@ -104,21 +104,21 @@ error: encountered more than one message field tag error: unexpected message field tag after compact options --> testdata/parser/def/ordering.proto:36:21 - | + | 36 | M x [foo = bar] = 1; - | ----------- ^^^ + | ----------- ^^^ | | | previous compact options is here error: unexpected tokens in message definition --> testdata/parser/def/ordering.proto:37:23 - | + | 37 | M x { /* ... */ } = 1; | ^^^ expected identifier, `;`, `.`, `(...)`, or `{...}` error: encountered more than one compact options --> testdata/parser/def/ordering.proto:39:21 - | + | 39 | M x [foo = bar] [foo = bar]; | ----------- ^^^^^^^^^^^ help: consider removing this | | @@ -126,7 +126,7 @@ error: encountered more than one compact options error: unexpected `[...]` in message definition --> testdata/parser/def/ordering.proto:40:23 - | + | 40 | M x { /* ... */ } [foo = bar]; | ^^^^^^^^^^^ expected identifier, `;`, `.`, `(...)`, or `{...}` diff --git a/experimental/parser/testdata/parser/enum/incomplete.proto.stderr.txt b/experimental/parser/testdata/parser/enum/incomplete.proto.stderr.txt index d8b66d4e..65a25684 100644 --- a/experimental/parser/testdata/parser/enum/incomplete.proto.stderr.txt +++ b/experimental/parser/testdata/parser/enum/incomplete.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected `}` after definition --> testdata/parser/enum/incomplete.proto:22:1 - | + | 22 | } | ^ expected `;` diff --git a/experimental/parser/testdata/parser/expr.proto.stderr.txt b/experimental/parser/testdata/parser/expr.proto.stderr.txt index 64f029aa..b3f835ca 100644 --- a/experimental/parser/testdata/parser/expr.proto.stderr.txt +++ b/experimental/parser/testdata/parser/expr.proto.stderr.txt @@ -1,30 +1,30 @@ error: unexpected integer literal in message expression --> testdata/parser/expr.proto:45:22 - | + | 45 | option (test.bad) = {1, 2, 3}; | ^ expected message field value error: unexpected integer literal in message expression --> testdata/parser/expr.proto:45:25 - | + | 45 | option (test.bad) = {1, 2, 3}; | ^ expected message field value error: unexpected integer literal in message expression --> testdata/parser/expr.proto:45:28 - | + | 45 | option (test.bad) = {1, 2, 3}; | ^ expected message field value error: unexpected `;` after `-` --> testdata/parser/expr.proto:46:22 - | + | 46 | option (test.bad) = -; | ^ expected expression error: unexpected `;` after `to` --> testdata/parser/expr.proto:47:25 - | + | 47 | option (test.bad) = 1 to; | ^ expected expression diff --git a/experimental/parser/testdata/parser/field/incomplete.proto.stderr.txt b/experimental/parser/testdata/parser/field/incomplete.proto.stderr.txt index 19e196dd..a2e66554 100644 --- a/experimental/parser/testdata/parser/field/incomplete.proto.stderr.txt +++ b/experimental/parser/testdata/parser/field/incomplete.proto.stderr.txt @@ -1,18 +1,18 @@ error: unexpected identifier after definition --> testdata/parser/field/incomplete.proto:24:5 - | + | 24 | foo.Bar name; | ^^^ expected `;` error: unexpected integer literal in definition --> testdata/parser/field/incomplete.proto:25:18 - | + | 25 | foo.Bar name 1; | ^ expected `=` error: unexpected `}` after definition --> testdata/parser/field/incomplete.proto:27:1 - | + | 27 | } | ^ expected `;` diff --git a/experimental/parser/testdata/parser/field/options.proto.stderr.txt b/experimental/parser/testdata/parser/field/options.proto.stderr.txt index 14238d54..dd855fdd 100644 --- a/experimental/parser/testdata/parser/field/options.proto.stderr.txt +++ b/experimental/parser/testdata/parser/field/options.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected `:` in compact option --> testdata/parser/field/options.proto:27:19 - | + | 27 | M bad = 4 [foo: {bar: baz}]; | ^ help: replace this with `=` = note: top-level `option` assignment uses `=`, not `:` diff --git a/experimental/parser/testdata/parser/import/eof_after_kw.proto.stderr.txt b/experimental/parser/testdata/parser/import/eof_after_kw.proto.stderr.txt index dd52dbff..22329c3f 100644 --- a/experimental/parser/testdata/parser/import/eof_after_kw.proto.stderr.txt +++ b/experimental/parser/testdata/parser/import/eof_after_kw.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected end-of-file after import --> testdata/parser/import/eof_after_kw.proto:20:7 - | + | 20 | import | ^ expected `;` diff --git a/experimental/parser/testdata/parser/lists.proto.stderr.txt b/experimental/parser/testdata/parser/lists.proto.stderr.txt index e2c5c2d4..7cdfb903 100644 --- a/experimental/parser/testdata/parser/lists.proto.stderr.txt +++ b/experimental/parser/testdata/parser/lists.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected integer literal in array expression --> testdata/parser/lists.proto:20:20 - | + | 20 | option foo = [1, 2 3]; | ^ expected `,` | | @@ -8,7 +8,7 @@ error: unexpected integer literal in array expression error: unexpected extra `,` in array expression --> testdata/parser/lists.proto:21:20 - | + | 21 | option foo = [1, 2,, 3]; | -^ expected expression | | @@ -16,7 +16,7 @@ error: unexpected extra `,` in array expression error: unexpected extra `,` in array expression --> testdata/parser/lists.proto:22:20 - | + | 22 | option foo = [1, 2,, 3,]; | -^ expected expression | | @@ -24,19 +24,19 @@ error: unexpected extra `,` in array expression error: unexpected trailing `,` in array expression --> testdata/parser/lists.proto:22:23 - | + | 22 | option foo = [1, 2,, 3,]; - | ^ + | ^ error: unexpected leading `,` in array expression --> testdata/parser/lists.proto:23:15 - | + | 23 | option foo = [,1 2,, 3,]; | ^ expected expression error: unexpected integer literal in array expression --> testdata/parser/lists.proto:23:18 - | + | 23 | option foo = [,1 2,, 3,]; | ^ expected `,` | | @@ -44,7 +44,7 @@ error: unexpected integer literal in array expression error: unexpected extra `,` in array expression --> testdata/parser/lists.proto:23:20 - | + | 23 | option foo = [,1 2,, 3,]; | -^ expected expression | | @@ -52,25 +52,25 @@ error: unexpected extra `,` in array expression error: unexpected trailing `,` in array expression --> testdata/parser/lists.proto:23:23 - | + | 23 | option foo = [,1 2,, 3,]; - | ^ + | ^ error: unexpected `;` in array expression --> testdata/parser/lists.proto:24:16 - | + | 24 | option foo = [1; 2; 3]; | ^ expected `,` error: unexpected `;` in array expression --> testdata/parser/lists.proto:24:19 - | + | 24 | option foo = [1; 2; 3]; | ^ expected `,` error: unexpected message expression in array expression --> testdata/parser/lists.proto:25:17 - | + | 25 | option foo = [a {}]; | ^^ expected `,` | | @@ -78,13 +78,13 @@ error: unexpected message expression in array expression error: unexpected leading `,` in array expression --> testdata/parser/lists.proto:26:15 - | + | 26 | option foo = [,]; | ^ expected expression error: unexpected extra `;` in message expression --> testdata/parser/lists.proto:31:16 - | + | 31 | bar: 2;; | -^ expected message field value | | @@ -92,13 +92,13 @@ error: unexpected extra `;` in message expression error: unexpected leading `;` in message expression --> testdata/parser/lists.proto:34:15 - | + | 34 | option foo = {;bar: 1}; | ^ expected message field value error: unexpected extra `;` in message expression --> testdata/parser/lists.proto:35:22 - | + | 35 | option foo = {baz: 1;; baz: 1}; | -^ expected message field value | | @@ -106,7 +106,7 @@ error: unexpected extra `;` in message expression error: unexpected extra `;` in message expression --> testdata/parser/lists.proto:36:22 - | + | 36 | option foo = {baz: 1,; baz: 1;}; | -^ expected message field value | | @@ -114,19 +114,19 @@ error: unexpected extra `;` in message expression error: unexpected leading `;` in message expression --> testdata/parser/lists.proto:38:10 - | + | 38 | bar {;} | ^ expected message field value error: unexpected leading `,` in message expression --> testdata/parser/lists.proto:39:10 - | + | 39 | bar {,} | ^ expected message field value error: unexpected type name in method parameter list --> testdata/parser/lists.proto:45:17 - | + | 45 | rpc Foo(int int) returns (int int); | ^^^ expected `,` | | @@ -134,7 +134,7 @@ error: unexpected type name in method parameter list error: unexpected type name in method return type --> testdata/parser/lists.proto:45:35 - | + | 45 | rpc Foo(int int) returns (int int); | ^^^ expected `,` | | @@ -142,25 +142,25 @@ error: unexpected type name in method return type error: unexpected `;` in method parameter list --> testdata/parser/lists.proto:46:16 - | + | 46 | rpc Foo(int; int) returns (int, int,); | ^ expected `,` error: unexpected trailing `,` in method return type --> testdata/parser/lists.proto:46:40 - | + | 46 | rpc Foo(int; int) returns (int, int,); - | ^ + | ^ error: unexpected leading `,` in method parameter list --> testdata/parser/lists.proto:47:13 - | + | 47 | rpc Foo(, int, int) returns (int,, int,); | ^ expected type error: unexpected extra `,` in method return type --> testdata/parser/lists.proto:47:38 - | + | 47 | rpc Foo(, int, int) returns (int,, int,); | -^ expected type | | @@ -168,25 +168,25 @@ error: unexpected extra `,` in method return type error: unexpected trailing `,` in method return type --> testdata/parser/lists.proto:47:43 - | + | 47 | rpc Foo(, int, int) returns (int,, int,); - | ^ + | ^ error: unexpected `;` in method parameter list --> testdata/parser/lists.proto:48:13 - | + | 48 | rpc Foo(;) returns (,); | ^ expected type error: unexpected leading `,` in method return type --> testdata/parser/lists.proto:48:25 - | + | 48 | rpc Foo(;) returns (,); | ^ expected type error: unexpected type name in type parameters --> testdata/parser/lists.proto:55:13 - | + | 55 | map x; | ^^^ expected `,` | | @@ -194,7 +194,7 @@ error: unexpected type name in type parameters error: unexpected extra `,` in type parameters --> testdata/parser/lists.proto:56:13 - | + | 56 | map x; | -^ expected type | | @@ -202,31 +202,31 @@ error: unexpected extra `,` in type parameters error: unexpected leading `,` in type parameters --> testdata/parser/lists.proto:57:9 - | + | 57 | map<,> x; | ^ expected type error: unexpected leading `,` in type parameters --> testdata/parser/lists.proto:59:9 - | + | 59 | map<,int, int> x; | ^ expected type error: unexpected `;` in type parameters --> testdata/parser/lists.proto:60:12 - | + | 60 | map x; | ^ expected `,` error: unexpected trailing `,` in type parameters --> testdata/parser/lists.proto:63:12 - | + | 63 | int, - | ^ + | ^ error: unexpected integer literal in reserved range --> testdata/parser/lists.proto:68:19 - | + | 68 | reserved 1, 2 3; | ^ expected `,` | | @@ -234,7 +234,7 @@ error: unexpected integer literal in reserved range error: unexpected extra `,` in reserved range --> testdata/parser/lists.proto:69:19 - | + | 69 | reserved 1, 2,, 3; | -^ expected expression | | @@ -242,7 +242,7 @@ error: unexpected extra `,` in reserved range error: unexpected extra `,` in reserved range --> testdata/parser/lists.proto:70:19 - | + | 70 | reserved 1, 2,, 3,; | -^ expected expression | | @@ -250,19 +250,19 @@ error: unexpected extra `,` in reserved range error: unexpected trailing `,` in reserved range --> testdata/parser/lists.proto:70:22 - | + | 70 | reserved 1, 2,, 3,; - | ^ + | ^ error: unexpected leading `,` in reserved range --> testdata/parser/lists.proto:71:14 - | + | 71 | reserved ,1 2,, 3,; | ^ expected expression error: unexpected integer literal in reserved range --> testdata/parser/lists.proto:71:17 - | + | 71 | reserved ,1 2,, 3,; | ^ expected `,` | | @@ -270,7 +270,7 @@ error: unexpected integer literal in reserved range error: unexpected extra `,` in reserved range --> testdata/parser/lists.proto:71:19 - | + | 71 | reserved ,1 2,, 3,; | -^ expected expression | | @@ -278,13 +278,13 @@ error: unexpected extra `,` in reserved range error: unexpected trailing `,` in reserved range --> testdata/parser/lists.proto:71:22 - | + | 71 | reserved ,1 2,, 3,; - | ^ + | ^ error: unexpected message expression in reserved range --> testdata/parser/lists.proto:72:16 - | + | 72 | reserved a {}; | ^^ expected `,` | | @@ -292,13 +292,13 @@ error: unexpected message expression in reserved range error: unexpected leading `,` in reserved range --> testdata/parser/lists.proto:73:14 - | + | 73 | reserved ,; | ^ expected expression error: unexpected identifier in reserved range --> testdata/parser/lists.proto:75:19 - | + | 75 | reserved a, b c; | ^ expected `,` | | @@ -306,7 +306,7 @@ error: unexpected identifier in reserved range error: unexpected identifier in reserved range --> testdata/parser/lists.proto:76:19 - | + | 76 | reserved a, b c | ^ expected `,` | | @@ -314,7 +314,7 @@ error: unexpected identifier in reserved range error: unexpected `message` after reserved range --> testdata/parser/lists.proto:77:5 - | + | 77 | message Foo {} | ^^^^^^^ expected `;` diff --git a/experimental/parser/testdata/parser/package/42.proto.stderr.txt b/experimental/parser/testdata/parser/package/42.proto.stderr.txt index 5c151d49..068c4268 100644 --- a/experimental/parser/testdata/parser/package/42.proto.stderr.txt +++ b/experimental/parser/testdata/parser/package/42.proto.stderr.txt @@ -1,12 +1,12 @@ error: unexpected integer literal after `package` declaration --> testdata/parser/package/42.proto:17:9 - | + | 17 | package 42; | ^^ expected `;` error: unexpected integer literal in file scope --> testdata/parser/package/42.proto:17:9 - | + | 17 | package 42; | ^^ expected identifier, `;`, `.`, `(...)`, or `{...}` diff --git a/experimental/parser/testdata/parser/package/eof_after_kw.proto.stderr.txt b/experimental/parser/testdata/parser/package/eof_after_kw.proto.stderr.txt index 1ef240de..ef9ef905 100644 --- a/experimental/parser/testdata/parser/package/eof_after_kw.proto.stderr.txt +++ b/experimental/parser/testdata/parser/package/eof_after_kw.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected end-of-file after `package` declaration --> testdata/parser/package/eof_after_kw.proto:17:8 - | + | 17 | package | ^ expected `;` diff --git a/experimental/parser/testdata/parser/syntax/eof_after_eq.proto.stderr.txt b/experimental/parser/testdata/parser/syntax/eof_after_eq.proto.stderr.txt index 8c24e2e4..311e556b 100644 --- a/experimental/parser/testdata/parser/syntax/eof_after_eq.proto.stderr.txt +++ b/experimental/parser/testdata/parser/syntax/eof_after_eq.proto.stderr.txt @@ -1,7 +1,7 @@ error: unexpected end-of-file in expression --> testdata/parser/syntax/eof_after_eq.proto:15:9 - | -15 | syntax = + | +15 | syntax = | ^ expected expression encountered 1 error diff --git a/experimental/parser/testdata/parser/syntax/eof_after_kw.proto.stderr.txt b/experimental/parser/testdata/parser/syntax/eof_after_kw.proto.stderr.txt index faee0824..df781754 100644 --- a/experimental/parser/testdata/parser/syntax/eof_after_kw.proto.stderr.txt +++ b/experimental/parser/testdata/parser/syntax/eof_after_kw.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected end-of-file in `syntax` declaration --> testdata/parser/syntax/eof_after_kw.proto:15:7 - | + | 15 | syntax | ^ expected `=` diff --git a/experimental/parser/testdata/parser/syntax/lonely.proto.stderr.txt b/experimental/parser/testdata/parser/syntax/lonely.proto.stderr.txt index 697e1e55..03b49403 100644 --- a/experimental/parser/testdata/parser/syntax/lonely.proto.stderr.txt +++ b/experimental/parser/testdata/parser/syntax/lonely.proto.stderr.txt @@ -1,12 +1,12 @@ error: unexpected `;` in `edition` declaration --> testdata/parser/syntax/lonely.proto:15:8 - | + | 15 | edition; | ^ expected `=` error: unexpected `;` in `syntax` declaration --> testdata/parser/syntax/lonely.proto:17:10 - | + | 17 | syntax = ; | ^ expected expression diff --git a/experimental/parser/testdata/parser/type/generic.proto.stderr.txt b/experimental/parser/testdata/parser/type/generic.proto.stderr.txt index 5a2c5f1a..a8b5c7b4 100644 --- a/experimental/parser/testdata/parser/type/generic.proto.stderr.txt +++ b/experimental/parser/testdata/parser/type/generic.proto.stderr.txt @@ -1,6 +1,6 @@ error: unexpected type name in type parameters --> testdata/parser/type/generic.proto:37:13 - | + | 37 | set x13 = 13; | ^^^ expected `,` | | diff --git a/experimental/parser/testdata/parser/type/repeated.proto.stderr.txt b/experimental/parser/testdata/parser/type/repeated.proto.stderr.txt index 5a242fe3..b096bd89 100644 --- a/experimental/parser/testdata/parser/type/repeated.proto.stderr.txt +++ b/experimental/parser/testdata/parser/type/repeated.proto.stderr.txt @@ -1,18 +1,18 @@ error: missing `(...)` around method return type --> testdata/parser/type/repeated.proto:33:41 - | + | 33 | rpc X4(required optional M) returns stream optional M {} | ^^^^^^^^^^^^^^^^^ help: replace this with `(stream optional M)` error: missing `(...)` around method return type --> testdata/parser/type/repeated.proto:34:46 - | + | 34 | rpc X5(repeated repeated test.M) returns repeated stream .test.M {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with `(repeated stream .test.M)` error: missing `(...)` around method return type --> testdata/parser/type/repeated.proto:35:43 - | + | 35 | rpc X6(stream stream .test.M) returns stream repeated M {} | ^^^^^^^^^^^^^^^^^ help: replace this with `(stream repeated M)` diff --git a/experimental/report/renderer.go b/experimental/report/renderer.go index 50b46ce7..f35e7fc6 100644 --- a/experimental/report/renderer.go +++ b/experimental/report/renderer.go @@ -40,14 +40,25 @@ type Renderer struct { WarningsAreErrors bool // If set, remark diagnostics will be printed. - // - // Ignored by [Renderer.RenderDiagnostic]. ShowRemarks bool // If set, rendering a diagnostic will show the debug footer. ShowDebug bool } +// renderer contains shared state for a rendering operation, allowing e.g. +// allocations to be re-used and simplifying function signatures. +type renderer struct { + Renderer + writer + + ss styleSheet + + // The width, in columns, of the line number margin in the diagnostic + // currently being rendered. + margin int +} + // Render renders a diagnostic report. // // In addition to returning the rendering result, returns whether the report @@ -56,21 +67,32 @@ type Renderer struct { // On the other hand, the actual error-typed return is an error when writing to // the writer. func (r Renderer) Render(report *Report, out io.Writer) (errorCount, warningCount int, err error) { + state := &renderer{ + Renderer: r, + writer: writer{out: out}, + ss: newStyleSheet(r), + } + return state.render(report) +} + +// RenderString is a helper for calling [Renderer.Render] with a [strings.Builder]. +func (r Renderer) RenderString(report *Report) (text string, errorCount, warningCount int) { + var buf strings.Builder + e, w, _ := r.Render(report, &buf) + return buf.String(), e, w +} + +func (r *renderer) render(report *Report) (errorCount, warningCount int, err error) { for _, diagnostic := range report.Diagnostics { if !r.ShowRemarks && diagnostic.level == Remark { continue } - if _, err = fmt.Fprintln(out, r.diagnostic(report, diagnostic)); err != nil { + r.diagnostic(report, diagnostic) + if err := r.Flush(); err != nil { return errorCount, warningCount, err } - if !r.Compact { - if _, err = fmt.Fprintln(out); err != nil { - return errorCount, warningCount, err - } - } - switch { case diagnostic.level <= Error: errorCount++ @@ -86,47 +108,29 @@ func (r Renderer) Render(report *Report, out io.Writer) (errorCount, warningCoun return errorCount, warningCount, err } - ss := newStyleSheet(r) - - pluralize := func(count int, what string) string { - if count == 1 { - return "1 " + what - } - return fmt.Sprint(count, " ", what, "s") - } - - if errorCount > 0 { - if _, err = fmt.Fprint(out, ss.bError, "encountered ", pluralize(errorCount, "error")); err != nil { - return errorCount, warningCount, err - } - - if warningCount > 0 { - if _, err = fmt.Fprint(out, " and ", pluralize(warningCount, "warning")); err != nil { - return errorCount, warningCount, err - } - } - if _, err = fmt.Fprintln(out, ss.reset); err != nil { - return errorCount, warningCount, err - } - } else if warningCount > 0 { - if _, err = fmt.Fprintln(out, ss.bWarning, "encountered ", pluralize(warningCount, "warning")); err != nil { - return errorCount, warningCount, err - } + switch { + case errorCount > 0 && warningCount > 0: + fmt.Fprintf(r, "%sencountered %d error%v and %d warning%v\n%s", + r.ss.bError, + errorCount, plural(errorCount), warningCount, plural(warningCount), + r.ss.reset, + ) + case errorCount > 0: + fmt.Fprintf(r, "%sencountered %d error%v\n%s", + r.ss.bError, + errorCount, plural(errorCount), r.ss.reset, + ) + case warningCount > 0: + fmt.Fprintf(r, "%sencountered %d warning%v\n%s", + r.ss.bWarning, + warningCount, plural(warningCount), r.ss.reset, + ) } - - _, err = fmt.Fprint(out, ss.reset) - return errorCount, warningCount, err -} - -// RenderString is a helper for calling [Renderer.Render] with a [strings.Builder]. -func (r Renderer) RenderString(report *Report) (text string, errorCount, warningCount int) { - var buf strings.Builder - e, w, _ := r.Render(report, &buf) - return buf.String(), e, w + return errorCount, warningCount, r.Flush() } // diagnostic renders a single diagnostic to a string. -func (r Renderer) diagnostic(report *Report, d Diagnostic) string { +func (r *renderer) diagnostic(report *Report, d Diagnostic) { if report.Tracing > 0 { // If we're debugging diagnostic traces, and we panic, show where this // particular diagnostic was generated. This is useful for debugging @@ -155,53 +159,34 @@ func (r Renderer) diagnostic(report *Report, d Diagnostic) string { level = "remark" } - ss := newStyleSheet(r) - // For the simple style, we imitate the Go compiler. if r.Compact { + r.WriteString(r.ss.ColorForLevel(d.level)) primary := d.Primary() - - if primary.File == nil { - path := d.inFile - if path == "" { - return fmt.Sprintf( - "%s%s: %s%s", - ss.ColorForLevel(d.level), - level, - d.message, - ss.reset, - ) - } - - return fmt.Sprintf( - "%s%s: %s: %s%s", - ss.ColorForLevel(d.level), - level, - path, + switch { + case primary.File != nil: + start := primary.StartLoc() + fmt.Fprintf(r, "%s: %s:%d:%d: %s", + level, primary.Path(), + start.Line, start.Column, d.message, - ss.reset, ) + case d.inFile != "": + fmt.Fprintf(r, "%s: %s: %s", + level, d.inFile, d.message, + ) + default: + fmt.Fprintf(r, "%s: %s", level, d.message) } - - start := primary.StartLoc() - - return fmt.Sprintf( - "%s%s: %s:%d:%d: %s%s", - ss.ColorForLevel(d.level), - level, - primary.Path(), - start.Line, - start.Column, - d.message, - ss.reset, - ) + r.WriteString(r.ss.reset) + r.WriteString("\n") + return } // For the other styles, we imitate the Rust compiler. See // https://github.com/rust-lang/rustc-dev-guide/blob/master/src/diagnostics.md - var out strings.Builder - fmt.Fprint(&out, ss.BoldForLevel(d.level), level, ": ", d.message, ss.reset) + fmt.Fprint(r, r.ss.BoldForLevel(d.level), level, ": ", d.message, r.ss.reset) locations := make([][2]Location, len(d.snippets)) for i, snip := range d.snippets { @@ -222,8 +207,7 @@ func (r Renderer) diagnostic(report *Report, d Diagnostic) string { for _, loc := range locations { greatestLine = max(greatestLine, loc[1].Line) } - lineBarWidth := len(strconv.Itoa(greatestLine)) // Easier than messing with math.Log10() - lineBarWidth = max(2, lineBarWidth) + r.margin = max(2, len(strconv.Itoa(greatestLine))) // Easier than messing with math.Log10() // Render all the diagnostic windows. parts := slicesx.Partition(d.snippets, func(a, b *snippet) bool { @@ -237,9 +221,9 @@ func (r Renderer) diagnostic(report *Report, d Diagnostic) string { parts(func(i int, snippets []snippet) bool { if i == 0 || d.snippets[i-1].Path() != d.snippets[i].Path() { - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(&out, lineBarWidth) + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin) primary := snippets[0] start := locations[i][0] @@ -247,67 +231,67 @@ func (r Renderer) diagnostic(report *Report, d Diagnostic) string { if i == 0 { sep = "-->" } - fmt.Fprintf(&out, "%s %s:%d:%d\n", sep, primary.Path(), start.Line, start.Column) + fmt.Fprintf(r, "%s %s:%d:%d\n", sep, primary.Path(), start.Line, start.Column) } if len(snippets[0].edits) > 0 { if i > 0 { - out.WriteByte('\n') + r.WriteString("\n") } - suggestion(snippets[0], lineBarWidth, &ss, &out) + r.suggestion(snippets[0]) return true } // Add a blank line after the file. This gives the diagnostic window some // visual breathing room. - padBy(&out, lineBarWidth) - out.WriteString(" | ") + r.WriteSpaces(r.margin) + r.WriteString(" | ") window := buildWindow(d.level, locations[i:i+len(snippets)], snippets) - window.Render(lineBarWidth, &ss, &out) + r.window(window) return true }) // Render a remedial file name for spanless errors. if len(d.snippets) == 0 && d.inFile != "" { - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(&out, lineBarWidth-1) + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin - 1) - fmt.Fprintf(&out, "--> %s", d.inFile) + fmt.Fprintf(r, "--> %s", d.inFile) } // Render the footers. For simplicity we collect them into an array first. footers := make([][3]string, 0, len(d.notes)+len(d.help)+len(d.debug)) for _, note := range d.notes { - footers = append(footers, [3]string{ss.bRemark, "note", note}) + footers = append(footers, [3]string{r.ss.bRemark, "note", note}) } for _, help := range d.help { - footers = append(footers, [3]string{ss.bRemark, "help", help}) + footers = append(footers, [3]string{r.ss.bRemark, "help", help}) } if r.ShowDebug { for _, debug := range d.debug { - footers = append(footers, [3]string{ss.bError, "debug", debug}) + footers = append(footers, [3]string{r.ss.bError, "debug", debug}) } } for _, footer := range footers { - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(&out, lineBarWidth) - out.WriteString(" = ") - fmt.Fprint(&out, footer[0], footer[1], ": ", ss.reset) + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin) + r.WriteString(" = ") + fmt.Fprint(r, footer[0], footer[1], ": ", r.ss.reset) for i, line := range strings.Split(footer[2], "\n") { if i > 0 { - out.WriteByte('\n') - margin := lineBarWidth + 3 + len(footer[1]) + 2 - padBy(&out, margin) + r.WriteString("\n") + margin := r.margin + 3 + len(footer[1]) + 2 + r.WriteSpaces(margin) } - out.WriteString(line) + r.WriteString(line) } } - out.WriteString(ss.reset) - return out.String() + r.WriteString(r.ss.reset) + r.WriteString("\n\n") } const maxMultilinesPerWindow = 8 @@ -427,7 +411,7 @@ func buildWindow(level Level, locations [][2]Location, snippets []snippet) *wind return w } -func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) { +func (r *renderer) window(w *window) { // lineInfo is layout information for a single line of this window. There // is one lineInfo for each line of w.file.Text we intend to render, as // given by w.offsets. @@ -505,7 +489,7 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) // Arrange for a "sidebar prefix" for this line. This is determined by any sidebars that are // active on this line, even if they end on it. - sidebar := renderSidebar(sidebarLen, -1, -1, ss, cur.sidebar) + sidebar := r.sidebar(sidebarLen, -1, -1, cur.sidebar) // Lay out the physical underlines in reverse order. This will cause longer lines to be // laid out first, which will be overwritten by shorter ones. @@ -537,9 +521,9 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) parts(func(_ int, line []byte) bool { level := Level(line[0]) if line[0] == 0 { - out.WriteString(ss.reset) + out.WriteString(r.ss.reset) } else { - out.WriteString(ss.BoldForLevel(level)) + out.WriteString(r.ss.BoldForLevel(level)) } for range line { switch level { @@ -564,7 +548,7 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) } } underlines := strings.TrimRight(out.String(), " ") - cur.underlines = []string{sidebar + underlines + " " + ss.BoldForLevel(rightmost.level) + rightmost.message} + cur.underlines = []string{sidebar + underlines + " " + r.ss.BoldForLevel(rightmost.level) + rightmost.message} // Now, do all the other messages, one per line. For each message, we also // need to draw pipes (|) above each one to connect it to its underline. @@ -607,7 +591,7 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) if nonColorLen == col { // Two pipes may appear on the same column! // This is why this is in a conditional. - buf = append(buf, ss.BoldForLevel(ul.level)...) + buf = append(buf, r.ss.BoldForLevel(ul.level)...) buf = append(buf, '|') nonColorLen++ } @@ -624,7 +608,7 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) actualStart := ul.start - 1 for _, other := range rest[idx:] { if other.start <= ul.start { - actualStart += len(ss.BoldForLevel(ul.level)) + actualStart += len(r.ss.BoldForLevel(ul.level)) } } for len(buf) < actualStart+len(ul.message)+1 { @@ -686,7 +670,7 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) fallthrough case ml.end: // We need to be flush with the sidebar here, so we trim the trailing space. - sidebar := []byte(strings.TrimRight(renderSidebar(0, -1, prevStart, ss, cur.sidebar[:mlIdx+1]), " ")) + sidebar := []byte(strings.TrimRight(r.sidebar(0, -1, prevStart, cur.sidebar[:mlIdx+1]), " ")) // We also need to erase the bars of any multis that are before this multi // and start/end on the same line. @@ -697,7 +681,7 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) // any of them to measure how far we need to adjust the offset to get to the // pipe. We need to account for one escape per multiline, and also need to skip // past the color escape on the pipe we want to erase. - codeLen := len(ss.bAccent) + codeLen := len(r.ss.bAccent) idx := mlIdx*(2+codeLen) + codeLen if idx < len(sidebar) { sidebar[idx] = ' ' @@ -804,14 +788,14 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) slashAt = len(prevSidebar) - 1 } } - sidebar := renderSidebar(sidebarLen, lineno, slashAt, ss, cur.sidebar) + sidebar := r.sidebar(sidebarLen, lineno, slashAt, cur.sidebar) if i > 0 && !info[i-1].shouldEmit { // Generate a visual break if this is right after a real line. - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(out, lineBarWidth-2) - out.WriteString("... ") + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin - 2) + r.WriteString("... ") // Generate a sidebar as before but this time we want to look at the // last line that was actually emitted. @@ -823,26 +807,26 @@ func (w *window) Render(lineBarWidth int, ss *styleSheet, out *strings.Builder) slashAt = len(prevSidebar) - 1 } - out.WriteString(renderSidebar(sidebarLen, lineno, slashAt, ss, cur.sidebar)) + r.WriteString(r.sidebar(sidebarLen, lineno, slashAt, cur.sidebar)) } // Ok, we are definitely printing this line out. // // Note that sidebar already includes a trailing ss.reset for us. - fmt.Fprintf(out, "\n%s%*d | %s%s", ss.nAccent, lineBarWidth, lineno, sidebar, ss.reset) + fmt.Fprintf(r, "\n%s%*d | %s%s", r.ss.nAccent, r.margin, lineno, sidebar, r.ss.reset) lastEmit = lineno // Re-use the logic from width calculation to correctly format a line for // showing in a terminal. - stringWidth(0, line, false, out) + stringWidth(0, line, false, &r.writer) // If this happens to be an annotated line, this is when it gets annotated. for _, line := range cur.underlines { - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(out, lineBarWidth) - out.WriteString(" | ") - out.WriteString(line) + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin) + r.WriteString(" | ") + r.WriteString(line) } } } @@ -889,7 +873,7 @@ func cmpMultilines(a, b multiline) int { return b.end - a.end } -func renderSidebar(bars, lineno, slashAt int, ss *styleSheet, multis []*multiline) string { +func (r *renderer) sidebar(bars, lineno, slashAt int, multis []*multiline) string { var sidebar strings.Builder for i, ml := range multis { if ml == nil { @@ -897,7 +881,7 @@ func renderSidebar(bars, lineno, slashAt int, ss *styleSheet, multis []*multilin continue } - sidebar.WriteString(ss.BoldForLevel(ml.level)) + sidebar.WriteString(r.ss.BoldForLevel(ml.level)) switch { case slashAt == i: @@ -918,17 +902,17 @@ func renderSidebar(bars, lineno, slashAt int, ss *styleSheet, multis []*multilin } // suggestion renders a single suggestion window. -func suggestion(snip snippet, lineBarWidth int, ss *styleSheet, out *strings.Builder) { - out.WriteString(ss.nAccent) - padBy(out, lineBarWidth) - out.WriteString("help: ") - out.WriteString(snip.message) +func (r *renderer) suggestion(snip snippet) { + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin) + r.WriteString("help: ") + r.WriteString(snip.message) // Add a blank line after the file. This gives the diagnostic window some // visual breathing room. - out.WriteByte('\n') - padBy(out, lineBarWidth) - out.WriteString(" | ") + r.WriteString("\n") + r.WriteSpaces(r.margin) + r.WriteString(" | ") // When the suggestion spans multiple lines, we don't bother doing a by-the-rune // diff, because the result can be hard for users to understand how to apply @@ -971,9 +955,9 @@ func suggestion(snip snippet, lineBarWidth int, ss *styleSheet, out *strings.Bui // Draw the line as we would for an ordinary window, but prefix // each line with a the hunk's kind and color. - fmt.Fprintf(out, "\n%s%*d | %s%c%s %s", - ss.nAccent, lineBarWidth, lineno, - hunk.bold(ss), hunk.kind, hunk.color(ss), + fmt.Fprintf(r, "\n%s%*d | %s%c%s %s", + r.ss.nAccent, r.margin, lineno, + hunk.bold(&r.ss), hunk.kind, hunk.color(&r.ss), line, ) @@ -989,33 +973,33 @@ func suggestion(snip snippet, lineBarWidth int, ss *styleSheet, out *strings.Bui } } - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(out, lineBarWidth) - out.WriteString(" | ") + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin) + r.WriteString(" | ") return } span, hunks := hunkDiff(snip.Span, snip.edits) - fmt.Fprintf(out, "\n%s%*d | ", ss.nAccent, lineBarWidth, span.StartLoc().Line) + fmt.Fprintf(r, "\n%s%*d | ", r.ss.nAccent, r.margin, span.StartLoc().Line) var column int for _, hunk := range hunks { if hunk.content == "" { continue } - out.WriteString(hunk.color(ss)) + r.WriteString(hunk.color(&r.ss)) // Re-use the logic from width calculation to correctly format a line for // showing in a terminal. - column = stringWidth(column, hunk.content, false, out) + column = stringWidth(column, hunk.content, false, &r.writer) } // Draw underlines for each modified segment, using + and - as the // underline characters. - out.WriteByte('\n') - out.WriteString(ss.nAccent) - padBy(out, lineBarWidth) - out.WriteString(" | ") + r.WriteString("\n") + r.WriteString(r.ss.nAccent) + r.WriteSpaces(r.margin) + r.WriteString(" | ") column = 0 for _, hunk := range hunks { if hunk.content == "" { @@ -1024,9 +1008,9 @@ func suggestion(snip snippet, lineBarWidth int, ss *styleSheet, out *strings.Bui prev := column column = stringWidth(column, hunk.content, false, nil) - out.WriteString(hunk.bold(ss)) + r.WriteString(hunk.bold(&r.ss)) for i := 0; i < column-prev; i++ { - out.WriteRune(hunk.kind) + r.WriteString(string(hunk.kind)) } } } @@ -1044,12 +1028,6 @@ func adjustLineOffsets(text string, start, end int) (int, int) { return start, end } -func padBy(out *strings.Builder, spaces int) { - for i := 0; i < spaces; i++ { - out.WriteByte(' ') - } -} - func padByRune(out *strings.Builder, spaces int, r rune) { for i := 0; i < spaces; i++ { out.WriteRune(r) diff --git a/experimental/report/testdata/i18n.yaml.color.txt b/experimental/report/testdata/i18n.yaml.color.txt index 2257ba0a..ba46dde7 100755 --- a/experimental/report/testdata/i18n.yaml.color.txt +++ b/experimental/report/testdata/i18n.yaml.color.txt @@ -1,20 +1,20 @@ ⟨b.red⟩error: emoji, CJK, bidi⟨reset⟩ ⟨blu⟩ --> foo.proto:5:9 - | + | ⟨blu⟩ 5 | ⟨reset⟩message 🐈⬛ { ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^^^^^^^^⟨reset⟩ ⟨b.red⟩ ⟨blu⟩ 6 | ⟨reset⟩ string 黑猫 = 1; ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩----⟨reset⟩ ⟨b.blu⟩note: some surfaces render CJK as sub-two-column ⟨blu⟩ ::: bar.proto:1:9 - | + | ⟨blu⟩ 1 | ⟨reset⟩import "חתול שחור.proto"; ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩---------------⟨reset⟩ ⟨b.blu⟩bidi works if it's quoted, at least⟨reset⟩ ⟨b.red⟩error: bidi (Arabic, Hebrew, Farsi, etc) is broken in some contexts⟨reset⟩ ⟨blu⟩ --> foo.proto:7:10 - | + | ⟨blu⟩ 7 | ⟨reset⟩ string القطة السوداء = 2; ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^^^^⟨reset⟩ ⟨b.red⟩⟨reset⟩ -⟨b.red⟩encountered 2 errors⟨reset⟩ +⟨b.red⟩encountered 2 errors ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/i18n.yaml.fancy.txt b/experimental/report/testdata/i18n.yaml.fancy.txt index ef6b22fb..23706d44 100755 --- a/experimental/report/testdata/i18n.yaml.fancy.txt +++ b/experimental/report/testdata/i18n.yaml.fancy.txt @@ -1,19 +1,19 @@ error: emoji, CJK, bidi --> foo.proto:5:9 - | + | 5 | message 🐈⬛ { - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ 6 | string 黑猫 = 1; | ---- note: some surfaces render CJK as sub-two-column ::: bar.proto:1:9 - | + | 1 | import "חתול שחור.proto"; | --------------- bidi works if it's quoted, at least error: bidi (Arabic, Hebrew, Farsi, etc) is broken in some contexts --> foo.proto:7:10 - | + | 7 | string القطة السوداء = 2; - | ^^^^^^^^ + | ^^^^^^^^ encountered 2 errors diff --git a/experimental/report/testdata/multi-file.yaml.color.txt b/experimental/report/testdata/multi-file.yaml.color.txt index f9cb2ba7..282eeedf 100755 --- a/experimental/report/testdata/multi-file.yaml.color.txt +++ b/experimental/report/testdata/multi-file.yaml.color.txt @@ -1,29 +1,29 @@ ⟨b.red⟩error: two files⟨reset⟩ ⟨blu⟩ --> foo.proto:3:9 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^^^⟨reset⟩ ⟨b.red⟩foo ⟨blu⟩ 4 | ⟨reset⟩ ⟨blu⟩ 5 | ⟨reset⟩message Blah { ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩----⟨reset⟩ ⟨b.blu⟩bar ⟨blu⟩ ::: bar.proto:3:9 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz2; ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩-------⟨reset⟩ ⟨b.blu⟩baz⟨reset⟩ ⟨b.red⟩error: three files⟨reset⟩ ⟨blu⟩ --> foo.proto:3:9 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^^^⟨reset⟩ ⟨b.red⟩foo ⟨blu⟩ ::: bar.proto:3:9 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz2; ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩-------⟨reset⟩ ⟨b.blu⟩baz ⟨blu⟩ ::: foo.proto:5:9 - | + | ⟨blu⟩ 5 | ⟨reset⟩message Blah { ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩----⟨reset⟩ ⟨b.blu⟩bar⟨reset⟩ -⟨b.red⟩encountered 2 errors⟨reset⟩ +⟨b.red⟩encountered 2 errors ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/multi-file.yaml.fancy.txt b/experimental/report/testdata/multi-file.yaml.fancy.txt index 0f49e78e..a7bb1d9d 100755 --- a/experimental/report/testdata/multi-file.yaml.fancy.txt +++ b/experimental/report/testdata/multi-file.yaml.fancy.txt @@ -1,27 +1,27 @@ error: two files --> foo.proto:3:9 - | + | 3 | package abc.xyz; | ^^^^^^^ foo - 4 | + 4 | 5 | message Blah { | ---- bar ::: bar.proto:3:9 - | + | 3 | package abc.xyz2; | ------- baz error: three files --> foo.proto:3:9 - | + | 3 | package abc.xyz; | ^^^^^^^ foo ::: bar.proto:3:9 - | + | 3 | package abc.xyz2; | ------- baz ::: foo.proto:5:9 - | + | 5 | message Blah { | ---- bar diff --git a/experimental/report/testdata/multi-underline.yaml.color.txt b/experimental/report/testdata/multi-underline.yaml.color.txt index 7966e25e..29dd3a16 100755 --- a/experimental/report/testdata/multi-underline.yaml.color.txt +++ b/experimental/report/testdata/multi-underline.yaml.color.txt @@ -1,21 +1,21 @@ ⟨b.red⟩error: `size_t` is not a built-in Protobuf type⟨reset⟩ ⟨blu⟩ --> foo.proto:6:12 - | + | ⟨blu⟩ 1 | ⟨reset⟩syntax = "proto4" ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩--------⟨reset⟩ ⟨b.blu⟩syntax version specified here ⟨blu⟩ 2 | ⟨reset⟩ -⟨blu⟩... +⟨blu⟩... ⟨blu⟩ 6 | ⟨reset⟩ required size_t x = 0; ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^⟨reset⟩ ⟨b.red⟩⟨reset⟩ ⟨b.ylw⟩warning: these are pretty bad names⟨reset⟩ ⟨blu⟩ --> foo.proto:3:9 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨reset⟩ ⟨b.ylw⟩^^^^^^^⟨reset⟩ ⟨b.ylw⟩could be better ⟨blu⟩ 4 | ⟨reset⟩ ⟨blu⟩ 5 | ⟨reset⟩message Blah { ⟨blu⟩ | ⟨reset⟩ ⟨b.blu⟩----⟨reset⟩ ⟨b.blu⟩blah to you too!!⟨reset⟩ -⟨b.red⟩encountered 1 error and 1 warning⟨reset⟩ +⟨b.red⟩encountered 1 error and 1 warning ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/multi-underline.yaml.fancy.txt b/experimental/report/testdata/multi-underline.yaml.fancy.txt index 182366b7..3c824c10 100755 --- a/experimental/report/testdata/multi-underline.yaml.fancy.txt +++ b/experimental/report/testdata/multi-underline.yaml.fancy.txt @@ -1,19 +1,19 @@ error: `size_t` is not a built-in Protobuf type --> foo.proto:6:12 - | + | 1 | syntax = "proto4" | -------- syntax version specified here - 2 | -... + 2 | +... 6 | required size_t x = 0; - | ^^^^^ + | ^^^^^ warning: these are pretty bad names --> foo.proto:3:9 - | + | 3 | package abc.xyz; | ^^^^^^^ could be better - 4 | + 4 | 5 | message Blah { | ---- blah to you too!! diff --git a/experimental/report/testdata/multiline.yaml.color.txt b/experimental/report/testdata/multiline.yaml.color.txt index 95153fd6..b6d8c717 100755 --- a/experimental/report/testdata/multiline.yaml.color.txt +++ b/experimental/report/testdata/multiline.yaml.color.txt @@ -1,18 +1,18 @@ ⟨b.ylw⟩warning: whole block⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩/ ⟨reset⟩message Blah { -⟨blu⟩... ⟨b.ylw⟩| +⟨blu⟩... ⟨b.ylw⟩| ⟨blu⟩12 | ⟨b.ylw⟩| ⟨reset⟩} ⟨blu⟩ | ⟨b.ylw⟩\_^ this block⟨reset⟩ ⟨b.ylw⟩warning: nested blocks⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩/ ⟨reset⟩message Blah { ⟨blu⟩ 6 | ⟨b.ylw⟩| ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.ylw⟩| ⟨b.blu⟩/ ⟨reset⟩ message Bonk { -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| ⟨blu⟩11 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.ylw⟩| ⟨b.blu⟩\___- and this block ⟨blu⟩12 | ⟨b.ylw⟩| ⟨reset⟩} @@ -20,12 +20,12 @@ ⟨b.ylw⟩warning: parallel blocks⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩/ ⟨reset⟩message Blah { ⟨blu⟩ 6 | ⟨b.ylw⟩| ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.ylw⟩| ⟨reset⟩ message Bonk { ⟨blu⟩ | ⟨b.ylw⟩\__^ this block -⟨blu⟩... ⟨b.blu⟩ +⟨blu⟩... ⟨b.blu⟩ ⟨blu⟩11 | ⟨b.blu⟩ ⟨reset⟩ } ⟨blu⟩ | ⟨b.blu⟩ ___- ⟨blu⟩12 | ⟨b.blu⟩/ ⟨reset⟩} @@ -33,9 +33,9 @@ ⟨b.ylw⟩warning: nested blocks same start⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩/ ⟨b.blu⟩/ ⟨reset⟩message Blah { -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| ⟨blu⟩11 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.ylw⟩| ⟨b.blu⟩\___- and this block ⟨blu⟩12 | ⟨b.ylw⟩| ⟨reset⟩} @@ -43,22 +43,22 @@ ⟨b.ylw⟩warning: nested blocks same end⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩/ ⟨reset⟩message Blah { ⟨blu⟩ 6 | ⟨b.ylw⟩| ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.ylw⟩| ⟨b.blu⟩/ ⟨reset⟩ message Bonk { -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| ⟨blu⟩12 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩} ⟨blu⟩ | ⟨b.ylw⟩\___^ this block ⟨blu⟩ | ⟨b.ylw⟩ ⟨b.blu⟩\_- and this block⟨reset⟩ ⟨b.ylw⟩warning: nested overlap⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩/ ⟨reset⟩message Blah { ⟨blu⟩ 6 | ⟨b.ylw⟩| ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.ylw⟩| ⟨b.blu⟩/ ⟨reset⟩ message Bonk { -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩| ⟨blu⟩11 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.ylw⟩\_____^ this block ⟨blu⟩12 | ⟨b.blu⟩| ⟨reset⟩} @@ -66,13 +66,13 @@ ⟨b.ylw⟩warning: nesting just the braces⟨reset⟩ ⟨blu⟩ --> foo.proto:5:15 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩ ⟨reset⟩message Blah { ⟨blu⟩ | ⟨b.ylw⟩ ________________^ ⟨blu⟩ 6 | ⟨b.ylw⟩/ ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.ylw⟩| ⟨b.blu⟩ ⟨reset⟩ message Bonk { ⟨blu⟩ | ⟨b.ylw⟩| ⟨b.blu⟩ ________________- -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩/ +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩/ ⟨blu⟩11 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.ylw⟩| ⟨b.blu⟩\___- and this block ⟨blu⟩12 | ⟨b.ylw⟩| ⟨reset⟩} @@ -80,11 +80,11 @@ ⟨b.ylw⟩warning: nesting just the braces same start⟨reset⟩ ⟨blu⟩ --> foo.proto:5:15 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩ ⟨b.blu⟩ ⟨reset⟩message Blah { ⟨blu⟩ | ⟨b.ylw⟩ ________________^ ⟨blu⟩ | ⟨b.ylw⟩/ ⟨b.blu⟩ ______________- -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩/ +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩/ ⟨blu⟩11 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.ylw⟩| ⟨b.blu⟩\___- and this block ⟨blu⟩12 | ⟨b.ylw⟩| ⟨reset⟩} @@ -92,11 +92,11 @@ ⟨b.ylw⟩warning: nesting just the braces same start (2)⟨reset⟩ ⟨blu⟩ --> foo.proto:5:15 - | + | ⟨blu⟩ 5 | ⟨b.blu⟩ ⟨b.ylw⟩ ⟨reset⟩message Blah { ⟨blu⟩ | ⟨b.blu⟩ ________________- ⟨blu⟩ | ⟨b.blu⟩/ ⟨b.ylw⟩ ______________^ -⟨blu⟩... ⟨b.blu⟩| ⟨b.ylw⟩/ +⟨blu⟩... ⟨b.blu⟩| ⟨b.ylw⟩/ ⟨blu⟩11 | ⟨b.blu⟩| ⟨b.ylw⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.blu⟩| ⟨b.ylw⟩\___^ and this block ⟨blu⟩12 | ⟨b.blu⟩| ⟨reset⟩} @@ -104,13 +104,13 @@ ⟨b.ylw⟩warning: braces nesting overlap⟨reset⟩ ⟨blu⟩ --> foo.proto:5:15 - | + | ⟨blu⟩ 5 | ⟨b.ylw⟩ ⟨reset⟩message Blah { ⟨blu⟩ | ⟨b.ylw⟩ ________________^ ⟨blu⟩ 6 | ⟨b.ylw⟩/ ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.ylw⟩| ⟨b.blu⟩ ⟨reset⟩ message Bonk { ⟨blu⟩ | ⟨b.ylw⟩| ⟨b.blu⟩ ________________- -⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩/ +⟨blu⟩... ⟨b.ylw⟩| ⟨b.blu⟩/ ⟨blu⟩11 | ⟨b.ylw⟩| ⟨b.blu⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.ylw⟩\_____^ this block ⟨blu⟩12 | ⟨b.blu⟩| ⟨reset⟩} @@ -118,17 +118,17 @@ ⟨b.ylw⟩warning: braces nesting overlap (2)⟨reset⟩ ⟨blu⟩ --> foo.proto:7:17 - | + | ⟨blu⟩ 5 | ⟨b.blu⟩ ⟨reset⟩message Blah { ⟨blu⟩ | ⟨b.blu⟩ ________________- ⟨blu⟩ 6 | ⟨b.blu⟩/ ⟨reset⟩ required size_t x = 0; ⟨blu⟩ 7 | ⟨b.blu⟩| ⟨b.ylw⟩ ⟨reset⟩ message Bonk { ⟨blu⟩ | ⟨b.blu⟩| ⟨b.ylw⟩ ________________^ -⟨blu⟩... ⟨b.blu⟩| ⟨b.ylw⟩/ +⟨blu⟩... ⟨b.blu⟩| ⟨b.ylw⟩/ ⟨blu⟩11 | ⟨b.blu⟩| ⟨b.ylw⟩| ⟨reset⟩ } ⟨blu⟩ | ⟨b.blu⟩\_____- this block ⟨blu⟩12 | ⟨b.ylw⟩| ⟨reset⟩} ⟨blu⟩ | ⟨b.ylw⟩\_^ and this block⟨reset⟩ -⟨b.ylw⟩ encountered 11 warnings +⟨b.ylw⟩encountered 11 warnings ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/multiline.yaml.fancy.txt b/experimental/report/testdata/multiline.yaml.fancy.txt index 6fb053a8..765b768c 100755 --- a/experimental/report/testdata/multiline.yaml.fancy.txt +++ b/experimental/report/testdata/multiline.yaml.fancy.txt @@ -1,18 +1,18 @@ warning: whole block --> foo.proto:5:1 - | + | 5 | / message Blah { -... | +... | 12 | | } | \_^ this block warning: nested blocks --> foo.proto:5:1 - | + | 5 | / message Blah { 6 | | required size_t x = 0; 7 | | / message Bonk { -... | | +... | | 11 | | | } | | \___- and this block 12 | | } @@ -20,12 +20,12 @@ warning: nested blocks warning: parallel blocks --> foo.proto:5:1 - | + | 5 | / message Blah { 6 | | required size_t x = 0; 7 | | message Bonk { | \__^ this block -... +... 11 | } | ___- 12 | / } @@ -33,9 +33,9 @@ warning: parallel blocks warning: nested blocks same start --> foo.proto:5:1 - | + | 5 | / / message Blah { -... | | +... | | 11 | | | } | | \___- and this block 12 | | } @@ -43,22 +43,22 @@ warning: nested blocks same start warning: nested blocks same end --> foo.proto:5:1 - | + | 5 | / message Blah { 6 | | required size_t x = 0; 7 | | / message Bonk { -... | | +... | | 12 | | | } | \___^ this block | \_- and this block warning: nested overlap --> foo.proto:5:1 - | + | 5 | / message Blah { 6 | | required size_t x = 0; 7 | | / message Bonk { -... | | +... | | 11 | | | } | \_____^ this block 12 | | } @@ -66,13 +66,13 @@ warning: nested overlap warning: nesting just the braces --> foo.proto:5:15 - | + | 5 | message Blah { | ________________^ 6 | / required size_t x = 0; 7 | | message Bonk { | | ________________- -... | / +... | / 11 | | | } | | \___- and this block 12 | | } @@ -80,11 +80,11 @@ warning: nesting just the braces warning: nesting just the braces same start --> foo.proto:5:15 - | + | 5 | message Blah { | ________________^ | / ______________- -... | / +... | / 11 | | | } | | \___- and this block 12 | | } @@ -92,11 +92,11 @@ warning: nesting just the braces same start warning: nesting just the braces same start (2) --> foo.proto:5:15 - | + | 5 | message Blah { | ________________- | / ______________^ -... | / +... | / 11 | | | } | | \___^ and this block 12 | | } @@ -104,13 +104,13 @@ warning: nesting just the braces same start (2) warning: braces nesting overlap --> foo.proto:5:15 - | + | 5 | message Blah { | ________________^ 6 | / required size_t x = 0; 7 | | message Bonk { | | ________________- -... | / +... | / 11 | | | } | \_____^ this block 12 | | } @@ -118,16 +118,16 @@ warning: braces nesting overlap warning: braces nesting overlap (2) --> foo.proto:7:17 - | + | 5 | message Blah { | ________________- 6 | / required size_t x = 0; 7 | | message Bonk { | | ________________^ -... | / +... | / 11 | | | } | \_____- this block 12 | | } | \_^ and this block - encountered 11 warnings +encountered 11 warnings diff --git a/experimental/report/testdata/no-snippets.yaml.color.txt b/experimental/report/testdata/no-snippets.yaml.color.txt index 9d229624..10b9e866 100755 --- a/experimental/report/testdata/no-snippets.yaml.color.txt +++ b/experimental/report/testdata/no-snippets.yaml.color.txt @@ -9,5 +9,5 @@ ⟨blu⟩ = ⟨b.cyn⟩help: ⟨reset⟩you should delete it to put it out of its misery ⟨blu⟩ = ⟨b.red⟩debug: ⟨reset⟩0xaaaaaaaaaaaaaaaa⟨reset⟩ -⟨b.red⟩encountered 2 errors and 1 warning⟨reset⟩ +⟨b.red⟩encountered 2 errors and 1 warning ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/single-line.yaml.color.txt b/experimental/report/testdata/single-line.yaml.color.txt index 8af91676..31bcd0a9 100755 --- a/experimental/report/testdata/single-line.yaml.color.txt +++ b/experimental/report/testdata/single-line.yaml.color.txt @@ -1,24 +1,24 @@ ⟨b.cyn⟩remark: "proto4" isn't real, it can't hurt you⟨reset⟩ ⟨blu⟩ --> foo.proto:1:10 - | + | ⟨blu⟩ 1 | ⟨reset⟩syntax = "proto4" ⟨blu⟩ | ⟨reset⟩ ⟨b.cyn⟩^^^^^^^^^⟨reset⟩ ⟨b.cyn⟩help: change this to "proto5"⟨reset⟩ ⟨b.red⟩error: missing `;`⟨reset⟩ ⟨blu⟩ --> foo.proto:1:18 - | + | ⟨blu⟩ 1 | ⟨reset⟩syntax = "proto4" ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^⟨reset⟩ ⟨b.red⟩here⟨reset⟩ ⟨b.cyn⟩remark: EOF⟨reset⟩ ⟨blu⟩ --> foo.proto:7:2 - | + | ⟨blu⟩ 7 | ⟨reset⟩} ⟨blu⟩ | ⟨reset⟩ ⟨b.cyn⟩^⟨reset⟩ ⟨b.cyn⟩here⟨reset⟩ ⟨b.red⟩error: package⟨reset⟩ ⟨blu⟩ --> foo.proto:3:1 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨b.red⟩^^^^^^^⟨reset⟩ ⟨b.blu⟩-⟨reset⟩ ⟨b.blu⟩semicolon ⟨blu⟩ | ⟨b.red⟩| @@ -26,7 +26,7 @@ ⟨b.red⟩error: this is an overlapping error⟨reset⟩ ⟨blu⟩ --> foo.proto:3:1 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨b.red⟩^^^^^^^⟨b.blu⟩---------⟨reset⟩ ⟨b.blu⟩package decl ⟨blu⟩ | ⟨b.red⟩| @@ -34,7 +34,7 @@ ⟨b.red⟩error: P A C K A G E⟨reset⟩ ⟨blu⟩ --> foo.proto:3:1 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨b.red⟩^⟨reset⟩ ⟨b.blu⟩--⟨reset⟩ ⟨b.blu⟩--⟨reset⟩ ⟨b.blu⟩help: ge ⟨blu⟩ | ⟨b.red⟩| ⟨b.blu⟩| @@ -44,7 +44,7 @@ ⟨b.red⟩error: P A C K A G E (different order)⟨reset⟩ ⟨blu⟩ --> foo.proto:3:3 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨b.blu⟩-⟨reset⟩ ⟨b.red⟩^^⟨reset⟩ ⟨b.blu⟩--⟨reset⟩ ⟨b.blu⟩help: ge ⟨blu⟩ | ⟨b.blu⟩| ⟨b.red⟩| @@ -54,7 +54,7 @@ ⟨b.red⟩error: P A C K A G E (single letters)⟨reset⟩ ⟨blu⟩ --> foo.proto:3:1 - | + | ⟨blu⟩ 3 | ⟨reset⟩package abc.xyz; ⟨blu⟩ | ⟨b.red⟩^⟨reset⟩ ⟨b.blu⟩--⟨reset⟩ ⟨b.blu⟩--⟨reset⟩ ⟨b.blu⟩g ⟨blu⟩ | ⟨b.red⟩| ⟨b.blu⟩| @@ -62,5 +62,5 @@ ⟨blu⟩ | ⟨b.blu⟩| ⟨blu⟩ | ⟨b.blu⟩k⟨reset⟩ -⟨b.red⟩encountered 6 errors⟨reset⟩ +⟨b.red⟩encountered 6 errors ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/single-line.yaml.fancy.txt b/experimental/report/testdata/single-line.yaml.fancy.txt index 85ceaa39..5eb4e05d 100755 --- a/experimental/report/testdata/single-line.yaml.fancy.txt +++ b/experimental/report/testdata/single-line.yaml.fancy.txt @@ -1,24 +1,24 @@ remark: "proto4" isn't real, it can't hurt you --> foo.proto:1:10 - | + | 1 | syntax = "proto4" | ^^^^^^^^^ help: change this to "proto5" error: missing `;` --> foo.proto:1:18 - | + | 1 | syntax = "proto4" | ^ here remark: EOF --> foo.proto:7:2 - | + | 7 | } | ^ here error: package --> foo.proto:3:1 - | + | 3 | package abc.xyz; | ^^^^^^^ - semicolon | | @@ -26,7 +26,7 @@ error: package error: this is an overlapping error --> foo.proto:3:1 - | + | 3 | package abc.xyz; | ^^^^^^^--------- package decl | | @@ -34,7 +34,7 @@ error: this is an overlapping error error: P A C K A G E --> foo.proto:3:1 - | + | 3 | package abc.xyz; | ^ -- -- help: ge | | | @@ -44,7 +44,7 @@ error: P A C K A G E error: P A C K A G E (different order) --> foo.proto:3:3 - | + | 3 | package abc.xyz; | - ^^ -- help: ge | | | @@ -54,7 +54,7 @@ error: P A C K A G E (different order) error: P A C K A G E (single letters) --> foo.proto:3:1 - | + | 3 | package abc.xyz; | ^ -- -- g | | | diff --git a/experimental/report/testdata/suggestions.yaml.color.txt b/experimental/report/testdata/suggestions.yaml.color.txt index 83683123..3e2017c6 100644 --- a/experimental/report/testdata/suggestions.yaml.color.txt +++ b/experimental/report/testdata/suggestions.yaml.color.txt @@ -1,45 +1,45 @@ ⟨b.cyn⟩remark: let protocompile pick a syntax for you⟨reset⟩ ⟨blu⟩ --> foo.proto:1:1 ⟨blu⟩ help: delete this - | + | ⟨blu⟩ 1 | ⟨b.red⟩-⟨red⟩ syntax = "proto3"; ⟨blu⟩ | ⟨reset⟩ ⟨b.cyn⟩remark: let protocompile pick a syntax for you⟨reset⟩ ⟨blu⟩ --> foo.proto:1:10 ⟨blu⟩ help: delete this - | + | ⟨blu⟩ 1 | ⟨b.red⟩-⟨red⟩ syntax = "proto3"; ⟨blu⟩ 1 | ⟨b.grn⟩+⟨grn⟩ syntax = ; ⟨blu⟩ | ⟨reset⟩ ⟨b.ylw⟩warning: services should have a `Service` suffix⟨reset⟩ ⟨blu⟩ --> foo.proto:5:9 - | + | ⟨blu⟩ 5 | ⟨reset⟩service Foo { ⟨blu⟩ | ⟨reset⟩ ⟨b.ylw⟩^^^⟨reset⟩ ⟨b.ylw⟩ ⟨blu⟩ help: change the name to `FooService` - | + | ⟨blu⟩ 5 | ⟨reset⟩service Foo⟨grn⟩Service⟨reset⟩ { ⟨blu⟩ | ⟨reset⟩ ⟨b.grn⟩+++++++⟨reset⟩ ⟨reset⟩ ⟨b.red⟩error: missing (...) around return type⟨reset⟩ ⟨blu⟩ --> foo.proto:6:31 - | + | ⟨blu⟩ 6 | ⟨reset⟩ rpc Get(GetRequest) returns GetResponse; ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^^^^^^^⟨reset⟩ ⟨b.red⟩ ⟨blu⟩ help: add `(...)` around the type - | + | ⟨blu⟩ 6 | ⟨reset⟩ rpc Get(GetRequest) returns ⟨grn⟩(⟨reset⟩GetResponse⟨grn⟩)⟨reset⟩; ⟨blu⟩ | ⟨reset⟩ ⟨b.grn⟩+⟨reset⟩ ⟨b.grn⟩+⟨reset⟩ ⟨reset⟩ ⟨b.red⟩error: method options must go in a block⟨reset⟩ ⟨blu⟩ --> foo.proto:7:45 - | + | ⟨blu⟩ 7 | ⟨reset⟩ rpc Put(PutRequest) returns (PutResponse) [foo = bar]; ⟨blu⟩ | ⟨reset⟩ ⟨b.red⟩^^^^^^^^^^^⟨reset⟩ ⟨b.red⟩compact options not allowed here ⟨blu⟩ help: use `option` settings inside of the method body - | + | ⟨blu⟩ 7 | ⟨b.red⟩-⟨red⟩ rpc Put(PutRequest) returns (PutResponse) [foo = bar]; ⟨blu⟩ 7 | ⟨b.grn⟩+⟨grn⟩ rpc Put(PutRequest) returns (PutResponse) { ⟨blu⟩ 8 | ⟨b.grn⟩+⟨grn⟩ option foo = bar; @@ -48,8 +48,8 @@ ⟨b.red⟩error: delete some stuff⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 -⟨blu⟩ help: - | +⟨blu⟩ help: + | ⟨blu⟩ 5 | ⟨b.red⟩-⟨red⟩ service Foo { ⟨blu⟩ 6 | ⟨reset⟩ ⟨reset⟩ rpc Get(GetRequest) returns GetResponse; ⟨blu⟩ 7 | ⟨reset⟩ ⟨reset⟩ rpc Put(PutRequest) returns (PutResponse) [foo = bar]; @@ -58,10 +58,10 @@ ⟨b.red⟩error: delete this method⟨reset⟩ ⟨blu⟩ --> foo.proto:5:1 -⟨blu⟩ help: - | +⟨blu⟩ help: + | ⟨blu⟩ 7 | ⟨b.red⟩-⟨red⟩ rpc Put(PutRequest) returns (PutResponse) [foo = bar]; ⟨blu⟩ | ⟨reset⟩ -⟨b.red⟩encountered 4 errors and 1 warning⟨reset⟩ +⟨b.red⟩encountered 4 errors and 1 warning ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/suggestions.yaml.fancy.txt b/experimental/report/testdata/suggestions.yaml.fancy.txt index 4dc5b31b..79cbc379 100644 --- a/experimental/report/testdata/suggestions.yaml.fancy.txt +++ b/experimental/report/testdata/suggestions.yaml.fancy.txt @@ -1,66 +1,66 @@ remark: let protocompile pick a syntax for you --> foo.proto:1:1 help: delete this - | + | 1 | - syntax = "proto3"; - | + | remark: let protocompile pick a syntax for you --> foo.proto:1:10 help: delete this - | + | 1 | - syntax = "proto3"; 1 | + syntax = ; - | + | warning: services should have a `Service` suffix --> foo.proto:5:9 - | + | 5 | service Foo { - | ^^^ + | ^^^ help: change the name to `FooService` - | + | 5 | service FooService { - | +++++++ + | +++++++ error: missing (...) around return type --> foo.proto:6:31 - | + | 6 | rpc Get(GetRequest) returns GetResponse; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: add `(...)` around the type - | + | 6 | rpc Get(GetRequest) returns (GetResponse); - | + + + | + + error: method options must go in a block --> foo.proto:7:45 - | + | 7 | rpc Put(PutRequest) returns (PutResponse) [foo = bar]; | ^^^^^^^^^^^ compact options not allowed here help: use `option` settings inside of the method body - | + | 7 | - rpc Put(PutRequest) returns (PutResponse) [foo = bar]; 7 | + rpc Put(PutRequest) returns (PutResponse) { 8 | + option foo = bar; 9 | + } - | + | error: delete some stuff --> foo.proto:5:1 - help: - | + help: + | 5 | - service Foo { 6 | rpc Get(GetRequest) returns GetResponse; 7 | rpc Put(PutRequest) returns (PutResponse) [foo = bar]; 8 | - } - | + | error: delete this method --> foo.proto:5:1 - help: - | + help: + | 7 | - rpc Put(PutRequest) returns (PutResponse) [foo = bar]; - | + | encountered 4 errors and 1 warning diff --git a/experimental/report/testdata/tabstops.yaml.color.txt b/experimental/report/testdata/tabstops.yaml.color.txt index d7f786bd..4de37670 100755 --- a/experimental/report/testdata/tabstops.yaml.color.txt +++ b/experimental/report/testdata/tabstops.yaml.color.txt @@ -1,6 +1,6 @@ ⟨b.ylw⟩warning: tabstop⟨reset⟩ ⟨blu⟩ --> foo.proto:6:9 - | + | ⟨blu⟩ 6 | ⟨reset⟩ field ⟨blu⟩ | ⟨b.blu⟩--------⟨b.ylw⟩^^^^^⟨reset⟩ ⟨b.ylw⟩this is in front of some tabstops ⟨blu⟩ | ⟨b.blu⟩| @@ -8,11 +8,11 @@ ⟨b.ylw⟩warning: partial tabstop⟨reset⟩ ⟨blu⟩ --> foo.proto:7:2 - | + | ⟨blu⟩ 7 | ⟨reset⟩ field ⟨blu⟩ | ⟨b.blu⟩-⟨b.ylw⟩^^^⟨reset⟩ ⟨b.ylw⟩tabstop ⟨blu⟩ | ⟨b.blu⟩| ⟨blu⟩ | ⟨b.blu⟩spaces⟨reset⟩ -⟨b.ylw⟩ encountered 2 warnings +⟨b.ylw⟩encountered 2 warnings ⟨reset⟩ \ No newline at end of file diff --git a/experimental/report/testdata/tabstops.yaml.fancy.txt b/experimental/report/testdata/tabstops.yaml.fancy.txt index 954bf3bf..14da62f1 100755 --- a/experimental/report/testdata/tabstops.yaml.fancy.txt +++ b/experimental/report/testdata/tabstops.yaml.fancy.txt @@ -1,6 +1,6 @@ warning: tabstop --> foo.proto:6:9 - | + | 6 | field | --------^^^^^ this is in front of some tabstops | | @@ -8,10 +8,10 @@ warning: tabstop warning: partial tabstop --> foo.proto:7:2 - | + | 7 | field | -^^^ tabstop | | | spaces - encountered 2 warnings +encountered 2 warnings diff --git a/experimental/report/width.go b/experimental/report/width.go index a1ae6943..44f14c31 100644 --- a/experimental/report/width.go +++ b/experimental/report/width.go @@ -32,7 +32,7 @@ func NonPrint(r rune) bool { // stringWidth calculates the rendered width of text if placed at the given column, // accounting for tabstops. -func stringWidth(column int, text string, allowNonPrint bool, out *strings.Builder) int { +func stringWidth(column int, text string, allowNonPrint bool, out *writer) int { // We can't just use StringWidth, because that doesn't respect tabstops // correctly. for text != "" { @@ -81,7 +81,7 @@ func stringWidth(column int, text string, allowNonPrint bool, out *strings.Build tab := TabstopWidth - (column % TabstopWidth) column += tab if out != nil { - padBy(out, tab) + out.WriteSpaces(tab) } } } diff --git a/experimental/report/writer.go b/experimental/report/writer.go new file mode 100644 index 00000000..996785a4 --- /dev/null +++ b/experimental/report/writer.go @@ -0,0 +1,117 @@ +// Copyright 2020-2025 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package report + +import ( + "bytes" + "io" + "slices" + "unicode" + + "github.com/bufbuild/protocompile/internal/ext/stringsx" + "github.com/bufbuild/protocompile/internal/ext/unsafex" +) + +// writer implements low-level writing helpers, including a custom buffering +// routine to avoid printing trailing whitespace to the output. +type writer struct { + out io.Writer + buf []byte // Never contains a '\n' byte. + err error +} + +// Write implements [io.Writer]. +func (w *writer) Write(data []byte) (int, error) { + w.WriteString(unsafex.StringAlias(data)) + return len(data), nil +} + +func (w *writer) WriteSpaces(n int) { + w.buf = slices.Grow(w.buf, n) + const spaces = " " + for n > len(spaces) { + w.buf = append(w.buf, spaces...) + n -= len(spaces) + } + w.buf = append(w.buf, spaces[:n]...) +} + +func (w *writer) WriteString(data string) { + // Break the input along newlines; each time we're about to append a + // newline, discard all trailing whitespace that isn't a newline. + first := true + stringsx.Lines(data)(func(line string) bool { + if !first { + w.flush(true) + } + first = false + w.buf = append(w.buf, line...) + return true + }) +} + +// Flush flushes the buffer to the writer's output. +func (w *writer) Flush() error { + defer func() { w.err = nil }() + return w.flush(false) +} + +// flush is like [writer.Flush], but instead retains the error to be returned +// out of Flush later. This allows e.g. WriteString to call flush() without +// needing to return an error and complicating the rendering code. +// +// If withNewline is set, appends a newline to the data being written. +func (w *writer) flush(withNewline bool) error { + if w.err != nil { + return w.err + } + + orig := w.buf + w.buf = bytes.TrimRightFunc(w.buf, unicode.IsSpace) + if withNewline { + w.buf = append(w.buf, '\n') + } + + // NOTE: The contract for Write requires that it return len(buf) when + // the error is nil. This means that the length return only matters if + // we hit an error condition, which we treat as fatal anyways. + _, w.err = w.out.Write(w.buf) + + if withNewline { + w.buf = w.buf[:0] + return w.err + } + + // Delete everything up until the first space; we don't know if the caller + // intends to append more to the current line or not. + // + // Avoid slices.Delete because that includes an unnecessary bounds check and + // a call to clear(). + // + // gocritic has a noisy warning about writing a = append(b, ...). + w.buf = append(orig[:0], orig[len(w.buf):]...) //nolint:gocritic + return w.err +} + +// plural is a helper for printing out plurals of numbers. +type plural int + +// String implements [fmt.Stringer]. +func (p plural) String() string { + if p == 1 { + return "" + } + return "s" +} diff --git a/internal/ext/stringsx/stringsx.go b/internal/ext/stringsx/stringsx.go index f8200556..8d2d63f0 100644 --- a/internal/ext/stringsx/stringsx.go +++ b/internal/ext/stringsx/stringsx.go @@ -16,6 +16,8 @@ package stringsx import ( + "strings" + "github.com/bufbuild/protocompile/internal/ext/iterx" "github.com/bufbuild/protocompile/internal/ext/unsafex" "github.com/bufbuild/protocompile/internal/iter" @@ -51,3 +53,24 @@ func Bytes(s string) iter.Seq[byte] { } } } + +// Split is like [strings.Split], but returning an iterator instead of a slice. +func Split[Sep string | rune](s string, sep Sep) iter.Seq[string] { + r := string(sep) + return func(yield func(string) bool) { + for { + chunk, rest, found := strings.Cut(s, r) + s = rest + if !yield(chunk) || !found { + return + } + } + } +} + +// Lines returns an iterator over the lines in the given string. +// +// It is equivalent to Split(s, '\n'). +func Lines(s string) iter.Seq[string] { + return Split(s, '\n') +}