diff --git a/SourceGeneratorToolkit.Test/GeneratorBaseTests.cs b/SourceGeneratorToolkit.Test/GeneratorBaseTests.cs
index 7ff495f..4698c6b 100644
--- a/SourceGeneratorToolkit.Test/GeneratorBaseTests.cs
+++ b/SourceGeneratorToolkit.Test/GeneratorBaseTests.cs
@@ -78,6 +78,7 @@ public void AMalformedMetadataFileIsReportedRatherThanSwallowed()
[TestMethod]
[DataRow("\n", DisplayName = "LF")]
[DataRow("\r\n", DisplayName = "CRLF")]
+ [DataRow("\r", DisplayName = "CR")]
public void AMalformedMetadataFileIsReportedAtTheBadLine(string newLine)
{
string json = string.Join(newLine, "{", " \"things\":", " [1,,2]", "}");
@@ -90,7 +91,7 @@ public void AMalformedMetadataFileIsReportedAtTheBadLine(string newLine)
Assert.AreEqual(2, span.StartLinePosition.Line);
// System.Text.Json reports the position it had reached, which is the first comma of the pair.
- Assert.AreEqual(json.Split('\n')[2].IndexOf(",,", StringComparison.Ordinal), span.StartLinePosition.Character);
+ Assert.AreEqual(" [1,,2]".IndexOf(",,", StringComparison.Ordinal), span.StartLinePosition.Character);
}
[TestMethod]
diff --git a/SourceGeneratorToolkit/MetadataFile.cs b/SourceGeneratorToolkit/MetadataFile.cs
index 02ccee2..363f6ff 100644
--- a/SourceGeneratorToolkit/MetadataFile.cs
+++ b/SourceGeneratorToolkit/MetadataFile.cs
@@ -223,17 +223,31 @@ private Location LocationOf(JsonException ex)
}
long bytes = ex.BytePositionInLine ?? 0;
- while (bytes > 0 && index < Text.Length && Text[index] is not '\r' and not '\n')
+ while (bytes > 0 && index < Text.Length && !EndsLine(index))
{
int width = CharactersAt(index);
bytes -= Utf8Bytes(index, width);
index += width;
}
- int length = index < Text.Length && Text[index] is not '\r' and not '\n' ? 1 : 0;
+ int length = index < Text.Length && !EndsLine(index) ? 1 : 0;
return LocationAt(index, length);
}
+ ///
+ /// Says whether the character at an offset ends a line as System.Text.Json counts lines: a
+ /// \n, or the \r of a \r\n pair.
+ ///
+ /// The offset of the character.
+ /// when the character ends the line.
+ ///
+ /// A lone \r is not a line break to System.Text.Json, so in a file with CR-only line
+ /// endings it reports every position on line zero, measured from the start of the file. Stopping
+ /// at that \r would clamp the position to the end of the first line.
+ ///
+ private bool EndsLine(int index) =>
+ Text[index] == '\n' || (Text[index] == '\r' && index + 1 < Text.Length && Text[index + 1] == '\n');
+
///
/// Finds where a line starts, counting lines by \n line feeds as System.Text.Json does.
///