From d39a9297b96f4f6b350bfeaf061197b29b071655 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 18 Sep 2024 12:17:10 +0100 Subject: [PATCH] Render source snippet on location (#291) This is something I implemented in my project's test implementation and found very helpful on large source generator samples - rendering the actual source snippet as an output comment, with squiggly lines underlining the problematic section, similar to what you get when compiling in the console. Unfortunately, only one test in this repo uses locations from actual source trees, so it might be harder to see the benefits, but even this modified example should give an idea of what it looks like. --- ...ationTests.ConsistentLocation.verified.txt | 8 +++- src/Tests/SyntaxLocationTests.cs | 2 +- .../Converters/LocationConverter.cs | 37 +++++++++++++++++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/Tests/SyntaxLocationTests.ConsistentLocation.verified.txt b/src/Tests/SyntaxLocationTests.ConsistentLocation.verified.txt index d79effd..ec4d767 100644 --- a/src/Tests/SyntaxLocationTests.ConsistentLocation.verified.txt +++ b/src/Tests/SyntaxLocationTests.ConsistentLocation.verified.txt @@ -1 +1,7 @@ -: (1,0)-(1,14) \ No newline at end of file +/* +class Foo { +void Bar() { } +^^^^^^^^^^^^^^ +} +*/ +theFile.cs: (1,0)-(1,14) \ No newline at end of file diff --git a/src/Tests/SyntaxLocationTests.cs b/src/Tests/SyntaxLocationTests.cs index ad6b24f..915d1d3 100644 --- a/src/Tests/SyntaxLocationTests.cs +++ b/src/Tests/SyntaxLocationTests.cs @@ -11,7 +11,7 @@ void Bar() { } """; var source = SourceText.From(sourceText); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(source); + var syntaxTree = SyntaxFactory.ParseSyntaxTree(source, path: "theFile.cs"); var methodDeclarationSyntax = syntaxTree .GetRoot() diff --git a/src/Verify.SourceGenerators/Converters/LocationConverter.cs b/src/Verify.SourceGenerators/Converters/LocationConverter.cs index 4a16842..7f08bf1 100644 --- a/src/Verify.SourceGenerators/Converters/LocationConverter.cs +++ b/src/Verify.SourceGenerators/Converters/LocationConverter.cs @@ -1,6 +1,37 @@ class LocationConverter : WriteOnlyJsonConverter { - public override void Write(VerifyJsonWriter writer, Location value) => - writer.Serialize(value.GetMappedLineSpan()); -} \ No newline at end of file + const int contextLines = 1; + + public override void Write(VerifyJsonWriter writer, Location value) + { + var lineSpan = value.GetMappedLineSpan(); + + // Pretty-print the error with the source code snippet. + if (value.SourceTree is { } source) + { + var comment = new StringBuilder().AppendLine(); + var lines = source.GetText().Lines; + var startLine = Math.Max(lineSpan.StartLinePosition.Line - contextLines, 0); + var endLine = Math.Min(lineSpan.EndLinePosition.Line + contextLines, lines.Count - 1); + for (var lineIdx = startLine; lineIdx <= endLine; lineIdx++) + { + var line = lines[lineIdx]; + // print the source line + comment.AppendLine(line.ToString()); + // print squiggly line highlighting the location + if (line.Span.Intersection(value.SourceSpan) is { } intersection) + { + comment + .Append(' ', intersection.Start - line.Start) + .Append('^', intersection.Length) + .AppendLine(); + } + } + writer.WriteComment(comment.ToString()); + writer.WriteWhitespace("\n"); + } + + writer.Serialize(lineSpan); + } +}