Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ public static void Main()
Diagnostic.Error("Operator '/' cannot be applied to operands of type 'string' and 'int'")
.WithCode("CS0019")
.WithNote("Try changing the type")
.WithLabel(new Label("Demo/Files/Program.cs", new Location(3, 2), "Not sure what this is")
.WithLength(5)
.WithPriority(1)
.WithColor(Color.Yellow))
.WithLabel(new Label("Demo/Files/Program.cs", new Location(15, 23), "This is of type 'int'")
.WithContextLines(2)
.WithLength(3)
.WithPriority(1)
.WithColor(Color.Yellow))
Expand All @@ -33,6 +38,7 @@ public static void Main()
Diagnostic.Info("Fix formatting")
.WithCode("IDE0055"))
.WithLabel(new Label("Demo/Files/Program.cs", 174..176, "Code should not contain trailing whitespace")

.WithColor(Color.Blue));

// Markdown
Expand All @@ -44,6 +50,7 @@ public static void Main()
.WithLabel(new Label("Demo/Files/Example.md", 31..41, "Invalid markdown")
.WithColor(Color.Red))
.WithLabel(new Label("Demo/Files/Example.md", 251..270, "Did you mean 'Yabba dabba doo'?")
.WithContextLines(3)
.WithColor(Color.Yellow)));

// C++
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Error [CS0116]: Namespace cannot be empty
┌─[Program.cs]
1 │ using System;
· ────┬────
· ╰──────── Namespace is empty
└─
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Error [CS0019]: Operator '/' cannot be applied to operands of type 'string' and 'int'
┌─[Program.cs]
13 │ var foo = 1;
14 │ var bar = "lol";
15 │ var qux = foo / bar;
· ─┬─
· ╰──────── This is of type 'int'
└─
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Error [CS0019]: Operator '/' cannot be applied to operands of type 'string' and 'int'
┌─[Program.cs]
10 │ {
11 │ public static void Main()
12 │ {
13 │ var foo = 1;
14 │ var bar = "lol";
15 │ var qux = foo / bar;
· ─┬─
· ╰──────── This is of type 'int'
└─
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Error [CS0019]: Operator '/' cannot be applied to operands of type 'string' and 'int'
┌─[Program.cs]
12 │ {
13 │ var foo = 1;
14 │ var bar = "lol";
15 │ var qux = foo / bar;
· ─┬─
· ╰──────── This is of type 'int'
└─
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Error [CS0019]: Operator '/' cannot be applied to operands of type 'string' and 'int'
NOTE: Try changing the type
┌─[Program.cs]
13 │ var foo = 1;
14 │ var bar = "lol";
15 │ var qux = foo / bar;
· ─┬─ ┬ ─┬─
· ╰──────── This is of type 'int'
· │ │
· ╰───── Division is not possible
· │
· ╰── This is of type 'string'
└─
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Error [CS0019]: Operator '/' cannot be applied to operands of type 'string' and 'int'
┌─[Program.cs]
14 │ var bar = "lol";
15 │ var qux = foo / bar;
· ─┬─
· ╰──────── This is of type 'int'
└─
52 changes: 52 additions & 0 deletions src/Errata.Tests/LabelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,56 @@ public void Should_Set_Note_To_Null_If_Null_Is_Provided()
label.Note.ShouldBeNull();
}
}

public sealed class TheWithContextMethod
{
[Fact]
public void Should_Set_ContextLines_To_Provided_Value()
{
// Given
var label = new Label("Program.cs", new Location(1, 2), "The message");

// When
label.WithContextLines(3);

// Then
label.ContextLines.ShouldBe(3);
}

[Fact]
public void Should_Return_Same_Instance_For_Chaining()
{
// Given
var label = new Label("Program.cs", new Location(1, 2), "The message");

// When
var result = label.WithContextLines(2);

// Then
result.ShouldBeSameAs(label);
}

[Fact]
public void Should_Throw_If_Negative_Value_Is_Provided()
{
// Given
var label = new Label("Program.cs", new Location(1, 2), "The message");

// When, Then
Should.Throw<System.ArgumentOutOfRangeException>(() => label.WithContextLines(-1));
}

[Fact]
public void Should_Allow_Zero_Context_Lines()
{
// Given
var label = new Label("Program.cs", new Location(1, 2), "The message");

// When
label.WithContextLines(0);

// Then
label.ContextLines.ShouldBe(0);
}
}
}
154 changes: 154 additions & 0 deletions src/Errata.Tests/ReportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -462,4 +462,158 @@ public Task Should_Render_Errata_Errors_Correctly()
return Verifier.Verify(console.Output);
}
}

[ExpectationPath("Context")]
public sealed class WithContextLines
{
[Fact]
[Expectation("SingleContext")]
public Task Should_Render_Label_With_One_Context_Line_Correctly()
{
// Given
var console = new TestConsole().Width(80);
var report = new Report(new EmbeddedResourceRepository());

report.AddDiagnostic(
Diagnostic.Error("Operator '/' cannot be applied to operands of type 'string' and 'int'")
.WithCode("CS0019")
.WithLabel(new Label("Program.cs", new Location(15, 23), "This is of type 'int'")
.WithColor(Color.Yellow)
.WithLength(3)
.WithContextLines(1)));

// When
report.Render(console);

// Then
return Verifier.Verify(console.Output);
}

[Fact]
[Expectation("MultipleContext")]
public Task Should_Render_Label_With_Multiple_Context_Lines_Correctly()
{
// Given
var console = new TestConsole().Width(80);
var report = new Report(new EmbeddedResourceRepository());

report.AddDiagnostic(
Diagnostic.Error("Operator '/' cannot be applied to operands of type 'string' and 'int'")
.WithCode("CS0019")
.WithLabel(new Label("Program.cs", new Location(15, 23), "This is of type 'int'")
.WithColor(Color.Yellow)
.WithLength(3)
.WithContextLines(3)));

// When
report.Render(console);

// Then
return Verifier.Verify(console.Output);
}

[Fact]
[Expectation("MultipleLabelsWithContext")]
public Task Should_Render_Multiple_Labels_With_Different_Context_Lines_Correctly()
{
// Given
var console = new TestConsole().Width(80);
var report = new Report(new EmbeddedResourceRepository());

report.AddDiagnostic(
Diagnostic.Error("Operator '/' cannot be applied to operands of type 'string' and 'int'")
.WithCode("CS0019")
.WithNote("Try changing the type")
.WithLabel(new Label("Program.cs", new Location(15, 23), "This is of type 'int'")
.WithColor(Color.Yellow)
.WithLength(3)
.WithContextLines(2))
.WithLabel(new Label("Program.cs", new Location(15, 27), "Division is not possible")
.WithColor(Color.Red)
.WithLength(1))
.WithLabel(new Label("Program.cs", new Location(15, 29), "This is of type 'string'")
.WithColor(Color.Blue)
.WithLength(3)));

// When
report.Render(console);

// Then
return Verifier.Verify(console.Output);
}

[Fact]
[Expectation("ContextAtStartOfFile")]
public Task Should_Render_Label_With_Context_At_Start_Of_File_Correctly()
{
// Given
var console = new TestConsole().Width(80);
var report = new Report(new EmbeddedResourceRepository());

report.AddDiagnostic(
Diagnostic.Error("Namespace cannot be empty")
.WithCode("CS0116")
.WithLabel(new Label("Program.cs", new Location(1, 1), "Namespace is empty")
.WithColor(Color.Red)
.WithLength(9)
.WithContextLines(5)));

// When
report.Render(console);

// Then
return Verifier.Verify(console.Output);
}

[Fact]
[Expectation("DefaultContextLines")]
public Task Should_Apply_Default_Context_Lines_From_Settings()
{
// Given
var console = new TestConsole().Width(80);
var report = new Report(new EmbeddedResourceRepository());

report.AddDiagnostic(
Diagnostic.Error("Operator '/' cannot be applied to operands of type 'string' and 'int'")
.WithCode("CS0019")
.WithLabel(new Label("Program.cs", new Location(15, 23), "This is of type 'int'")
.WithColor(Color.Yellow)
.WithLength(3)));

// When - Set default context lines to 2
report.Render(console, new ReportSettings
{
DefaultContextLines = 2,
});

// Then
return Verifier.Verify(console.Output);
}

[Fact]
[Expectation("DefaultContextLinesOverride")]
public Task Should_Allow_Label_To_Override_Default_Context_Lines()
{
// Given
var console = new TestConsole().Width(80);
var report = new Report(new EmbeddedResourceRepository());

report.AddDiagnostic(
Diagnostic.Error("Operator '/' cannot be applied to operands of type 'string' and 'int'")
.WithCode("CS0019")
.WithLabel(new Label("Program.cs", new Location(15, 23), "This is of type 'int'")
.WithColor(Color.Yellow)
.WithLength(3)
.WithContextLines(5))); // Override default with 5

// When - Set default context lines to 2
report.Render(console, new ReportSettings
{
DefaultContextLines = 2,
});

// Then
return Verifier.Verify(console.Output);
}
}
}
5 changes: 5 additions & 0 deletions src/Errata/Label.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public sealed class Label
/// </summary>
public int Priority { get; set; } = 0;

/// <summary>
/// Gets or sets the number of additional context lines to display above this label.
/// </summary>
public int ContextLines { get; set; } = 0;

#if NET6_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="Label"/> class.
Expand Down
22 changes: 22 additions & 0 deletions src/Errata/LabelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,26 @@ public static Label WithPriority(this Label label, int priority)
label.Priority = priority;
return label;
}

/// <summary>
/// Sets the number of additional context lines to display above this label.
/// </summary>
/// <param name="label">The label.</param>
/// <param name="lines">The number of lines above the label to display as context.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Label WithContextLines(this Label label, int lines)
{
if (label is null)
{
throw new ArgumentNullException(nameof(label));
}

if (lines < 0)
{
throw new ArgumentOutOfRangeException(nameof(lines), "Context lines must be greater than or equal to zero");
}

label.ContextLines = lines;
return label;
}
}
Loading