diff --git a/src/DiffEngineViewer.Tests/FsharpEntryTests.Screen.verified.txt b/src/DiffEngineViewer.Tests/FsharpEntryTests.Screen.verified.txt
new file mode 100644
index 00000000..5c5b3822
--- /dev/null
+++ b/src/DiffEngineViewer.Tests/FsharpEntryTests.Screen.verified.txt
@@ -0,0 +1,24 @@
++----------------------------------------------------------------------------------------------+
+| SampleTests.fs:42 inline 1 of 1 |
++----------------------+-----------------------------------+-----------------------------------+
+| Pending (1) | received | expected |
++----------------------+-----------------------------------+-----------------------------------+
+| > SampleTests.fs:42 | 1 the quick | 1 the quick |
+| | ~ 2 brown dog | ~ 2 brown fox |
+| | 3 jumps over | 3 jumps over |
+| | 4 the lazy | 4 the lazy |
+| | 5 dog | 5 dog |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
+| | | |
++----------------------+-----------------------------------+-----------------------------------+
+| [Accept] [Discard] [Accept all] lines 1-5 of 5 |
++----------------------------------------------------------------------------------------------+
\ No newline at end of file
diff --git a/src/DiffEngineViewer.Tests/FsharpEntryTests.cs b/src/DiffEngineViewer.Tests/FsharpEntryTests.cs
new file mode 100644
index 00000000..1c8d688f
--- /dev/null
+++ b/src/DiffEngineViewer.Tests/FsharpEntryTests.cs
@@ -0,0 +1,54 @@
+///
+/// An entry from a producer whose language does not implement CallerArgumentExpression, which is
+/// F#. There is no source text for the previous argument, so the patch carries its value instead -
+/// the same snapshot the expression would have parsed to, and the same anchor one parse further
+/// on.
+///
+public class FsharpEntryTests
+{
+ [Test]
+ public async Task The_previous_snapshot_fills_the_expected_pane()
+ {
+ var state = Fixtures.Inline(Patch());
+
+ var entry = state.Queue[0];
+
+ await Assert.That(entry.RightHeader).IsEqualTo("expected");
+ await Assert.That(entry.RightText).IsEqualTo(Fixtures.Expected);
+ await Assert.That(entry.Warning).IsNull();
+ }
+
+ ///
+ /// With neither anchor there is genuinely nothing to compare against, which is what the empty
+ /// side is for.
+ ///
+ [Test]
+ public async Task A_first_run_still_reads_as_a_new_snapshot()
+ {
+ var state = Fixtures.Inline(
+ new InlinePatch("SampleTests.fs", 42, null, Fixtures.Received)
+ {
+ TestName = null
+ });
+
+ var entry = state.Queue[0];
+
+ await Assert.That(entry.RightHeader).IsEqualTo("expected (new snapshot)");
+ await Assert.That(entry.RightText).IsEmpty();
+ }
+
+ ///
+ /// And what that looks like: two full panes with one line differing, rather than five added
+ /// lines beside nothing.
+ ///
+ [Test]
+ public Task Screen() =>
+ Verify(Fixtures.Render(Fixtures.Inline(Patch())));
+
+ static InlinePatch Patch() =>
+ new("SampleTests.fs", 42, null, Fixtures.Received)
+ {
+ TestName = null,
+ OriginalValue = Fixtures.Expected
+ };
+}
diff --git a/src/DiffEngineViewer/QueueEntry.cs b/src/DiffEngineViewer/QueueEntry.cs
index fce0b082..e54c06e5 100644
--- a/src/DiffEngineViewer/QueueEntry.cs
+++ b/src/DiffEngineViewer/QueueEntry.cs
@@ -198,7 +198,16 @@ public static QueueEntry ForDelete(
{
if (patch.OriginalExpression is null)
{
- return ("expected (new snapshot)", "", null);
+ // A producer whose language has no CallerArgumentExpression - F#, which does not
+ // implement it - anchors on the argument's value instead. It is the same snapshot the
+ // expression would have parsed to, so it is the same pane; without this every F#
+ // entry read as a new snapshot against an empty side, with every received line new.
+ if (patch.OriginalValue is null)
+ {
+ return ("expected (new snapshot)", "", null);
+ }
+
+ return ("expected", SourceLanguage.NormalizeNewlines(patch.OriginalValue), null);
}
// Read as the language of the file it came out of: an F# literal is not a C# one, and a