From e9bf37bf6af6d9cac7219b9f217dc35c86cff388 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 22 Jul 2026 21:31:40 +1000 Subject: [PATCH] Fix diff lines that were never rendered Fixes #7 Four defects caused changes to be silently omitted from the review output: - Empty lines rendered as nothing. The word wrap calculation produced a piece count of zero for empty text, so the render loop never ran and an added or removed blank line was invisible. - The last line of a snapshot could never render, because the range Stop is exclusive but was clamped to New.Count - 1. - Changes running to the end of a snapshot were dropped entirely. Ranges were only flushed upon encountering a following unchanged line, so a change with no trailing unchanged line was never emitted. - Line numbers drifted after the first insertion or deletion, since both columns used the index into the aligned diff rather than the position of each side within its own snapshot. --- src/Verify.Terminal.Tests/Data/Fourth/new | 3 +++ src/Verify.Terminal.Tests/Data/Fourth/old | 2 ++ src/Verify.Terminal.Tests/Data/Third/new | 3 +++ src/Verify.Terminal.Tests/Data/Third/old | 2 ++ .../Render.Output_First.verified.txt | 4 +-- .../Render.Output_Fourth.verified.txt | 10 +++++++ .../Render.Output_Second.verified.txt | 5 ++-- .../Render.Output_Third.verified.txt | 11 ++++++++ .../SnapshotRendererTests.cs | 2 ++ .../Verify.Terminal.Tests.csproj | 4 +++ src/Verify.Terminal/SnapshotDiff.cs | 11 +++++++- src/Verify.Terminal/SnapshotRenderer.cs | 26 +++++++++++-------- 12 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 src/Verify.Terminal.Tests/Data/Fourth/new create mode 100644 src/Verify.Terminal.Tests/Data/Fourth/old create mode 100644 src/Verify.Terminal.Tests/Data/Third/new create mode 100644 src/Verify.Terminal.Tests/Data/Third/old create mode 100644 src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Fourth.verified.txt create mode 100644 src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Third.verified.txt diff --git a/src/Verify.Terminal.Tests/Data/Fourth/new b/src/Verify.Terminal.Tests/Data/Fourth/new new file mode 100644 index 0000000..fa58e34 --- /dev/null +++ b/src/Verify.Terminal.Tests/Data/Fourth/new @@ -0,0 +1,3 @@ +line one +line two +line three \ No newline at end of file diff --git a/src/Verify.Terminal.Tests/Data/Fourth/old b/src/Verify.Terminal.Tests/Data/Fourth/old new file mode 100644 index 0000000..4c00b39 --- /dev/null +++ b/src/Verify.Terminal.Tests/Data/Fourth/old @@ -0,0 +1,2 @@ +line one +line two \ No newline at end of file diff --git a/src/Verify.Terminal.Tests/Data/Third/new b/src/Verify.Terminal.Tests/Data/Third/new new file mode 100644 index 0000000..42aa69f --- /dev/null +++ b/src/Verify.Terminal.Tests/Data/Third/new @@ -0,0 +1,3 @@ +feat: add new feature + +[tag1] diff --git a/src/Verify.Terminal.Tests/Data/Third/old b/src/Verify.Terminal.Tests/Data/Third/old new file mode 100644 index 0000000..0b57195 --- /dev/null +++ b/src/Verify.Terminal.Tests/Data/Third/old @@ -0,0 +1,2 @@ +feat: add new feature +[tag1] diff --git a/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_First.verified.txt b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_First.verified.txt index db41186..3860413 100644 --- a/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_First.verified.txt +++ b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_First.verified.txt @@ -16,6 +16,6 @@ First.received.txt 12 │-│············message:·"Unexpected·character·'='", 12 │+│············severity:·Error, 13 │-│············severity:·Verbose, - 14 14 │ │········}, - 15 15 │ │····], + 14 13 │ │········}, + 15 14 │ │····], ─────────────┴─┴──────────────────────────────────────────────────────────────── \ No newline at end of file diff --git a/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Fourth.verified.txt b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Fourth.verified.txt new file mode 100644 index 0000000..407ed16 --- /dev/null +++ b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Fourth.verified.txt @@ -0,0 +1,10 @@ +──────────────────────────────────────────────────────────────────────────────── +Fourth.received.txt +──────────────────────────────────────────────────────────────────────────────── +-old snapshot ++new snapshot +───────────┬─┬────────────────────────────────────────────────────────────────── + 1 1 │ │line·one + 2 2 │ │line·two + 3 │+│line·three +───────────┴─┴────────────────────────────────────────────────────────────────── \ No newline at end of file diff --git a/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Second.verified.txt b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Second.verified.txt index e7a3962..6033e1f 100644 --- a/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Second.verified.txt +++ b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Second.verified.txt @@ -6,7 +6,8 @@ Second.received.txt ───────────┬─┬────────────────────────────────────────────────────────────────── 1 1 │ │+-Greeting----+ 2 │+│|·············| - 3 3 │ │|·Hello·World·| + 2 3 │ │|·Hello·World·| 4 │+│|·············| - 5 5 │ │+-------------+ + 3 5 │ │+-------------+ + 4 │-│ ───────────┴─┴────────────────────────────────────────────────────────────────── \ No newline at end of file diff --git a/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Third.verified.txt b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Third.verified.txt new file mode 100644 index 0000000..917dc65 --- /dev/null +++ b/src/Verify.Terminal.Tests/Expectations/Rendering/Render.Output_Third.verified.txt @@ -0,0 +1,11 @@ +──────────────────────────────────────────────────────────────────────────────── +Third.received.txt +──────────────────────────────────────────────────────────────────────────────── +-old snapshot ++new snapshot +───────────┬─┬────────────────────────────────────────────────────────────────── + 1 1 │ │feat:·add·new·feature + 2 │+│ + 2 3 │ │[tag1] + 3 4 │ │ +───────────┴─┴────────────────────────────────────────────────────────────────── \ No newline at end of file diff --git a/src/Verify.Terminal.Tests/SnapshotRendererTests.cs b/src/Verify.Terminal.Tests/SnapshotRendererTests.cs index 35e350a..069b66d 100644 --- a/src/Verify.Terminal.Tests/SnapshotRendererTests.cs +++ b/src/Verify.Terminal.Tests/SnapshotRendererTests.cs @@ -7,6 +7,8 @@ public class SnapshotRendererTests [Expectation("Render")] [InlineData("First")] [InlineData("Second")] + [InlineData("Third")] + [InlineData("Fourth")] public Task Should_Render_Correctly(string scenario) { // Given diff --git a/src/Verify.Terminal.Tests/Verify.Terminal.Tests.csproj b/src/Verify.Terminal.Tests/Verify.Terminal.Tests.csproj index e88f307..49bbe4a 100644 --- a/src/Verify.Terminal.Tests/Verify.Terminal.Tests.csproj +++ b/src/Verify.Terminal.Tests/Verify.Terminal.Tests.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/src/Verify.Terminal/SnapshotDiff.cs b/src/Verify.Terminal/SnapshotDiff.cs index e90163b..9f39098 100644 --- a/src/Verify.Terminal/SnapshotDiff.cs +++ b/src/Verify.Terminal/SnapshotDiff.cs @@ -34,8 +34,10 @@ public SnapshotDiff(Snapshot snapshot, List old, List @new { // Found an unchanged line after something that // had been modified or deleted. + // Stop is exclusive, so it must be allowed to reach New.Count, + // otherwise the last line of the snapshot is never rendered. var rangeStart = Math.Max(0, start.Value - contextLines); - var rangeEnd = Math.Min(index + contextLines, New.Count - 1); + var rangeEnd = Math.Min(index + contextLines, New.Count); ranges.Add((rangeStart, rangeEnd)); start = null; @@ -55,6 +57,13 @@ public SnapshotDiff(Snapshot snapshot, List old, List @new index++; } + if (start != null) + { + // The snapshot ended while still processing a modified or deleted + // line, so there is no trailing unchanged line to close the range. + ranges.Add((Math.Max(0, start.Value - contextLines), New.Count)); + } + if (ranges.Count == 0) { return new List<(int Start, int Stop)>(); diff --git a/src/Verify.Terminal/SnapshotRenderer.cs b/src/Verify.Terminal/SnapshotRenderer.cs index f715ce4..465607e 100644 --- a/src/Verify.Terminal/SnapshotRenderer.cs +++ b/src/Verify.Terminal/SnapshotRenderer.cs @@ -80,23 +80,23 @@ public IRenderable Render(SnapshotDiff diff, int contextLines) if (@new.Type == ChangeType.Modified) { // Modified lines - RenderLine(ctx, old, index, '-', Color.Red, true, false); - RenderLine(ctx, @new, index, '+', Color.Green, false, true); + RenderLine(ctx, old, old.Position, null, '-', Color.Red); + RenderLine(ctx, @new, null, @new.Position, '+', Color.Green); } else if (@new.Type == ChangeType.Inserted) { // Inserted - RenderLine(ctx, @new, index, '+', Color.Green, false, true); + RenderLine(ctx, @new, null, @new.Position, '+', Color.Green); } else if (@new.Type == ChangeType.Imaginary) { - // Modified lines - RenderLine(ctx, old, index, '-', Color.Red, true, false); + // Deleted + RenderLine(ctx, old, old.Position, null, '-', Color.Red); } else { // Unchanged - RenderLine(ctx, @new, index, ' ', Color.Grey, true, true); + RenderLine(ctx, @new, old.Position, @new.Position, ' ', Color.Grey); } } @@ -114,14 +114,18 @@ public IRenderable Render(SnapshotDiff diff, int contextLines) private void RenderLine( SnapshotRendererContext ctx, DiffPiece piece, - int index, char op, Color color, - bool showLeft, bool showRight) + int? leftPosition, int? rightPosition, char op, Color color) { - var leftLineNumber = showLeft ? (index + 1).ToString() : string.Empty; - var rightLineNumber = showRight ? (index + 1).ToString() : string.Empty; + // The positions come from the snapshots themselves rather than from the + // index into the aligned diff, which drifts once a line is added or removed. + var leftLineNumber = leftPosition?.ToString() ?? string.Empty; + var rightLineNumber = rightPosition?.ToString() ?? string.Empty; var maxWidth = _console.Profile.Width - (4 + ctx.LineNumberWidth + 4 + ctx.LineNumberWidth + 1 + 1 + 1 + 1); - var pieces = (piece.Text.Length / maxWidth) + ((piece.Text.Length % maxWidth) == 0 ? 0 : 1); + + // An empty line still occupies a single row. Without the minimum of one, + // added or removed blank lines would not be rendered at all. + var pieces = Math.Max(1, (piece.Text.Length / maxWidth) + ((piece.Text.Length % maxWidth) == 0 ? 0 : 1)); for (var i = 0; i < pieces; i++) {