diff --git a/OpenRA.Game/MiniYaml.cs b/OpenRA.Game/MiniYaml.cs index f5af94378f..ae3189eca8 100644 --- a/OpenRA.Game/MiniYaml.cs +++ b/OpenRA.Game/MiniYaml.cs @@ -168,9 +168,9 @@ namespace OpenRA var spaces = 0; var textStart = false; - ReadOnlySpan key = null; - ReadOnlySpan value = null; - ReadOnlySpan comment = null; + ReadOnlySpan key = default; + ReadOnlySpan value = default; + ReadOnlySpan comment = default; var location = new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo }; if (line.Length > 0) @@ -254,25 +254,29 @@ namespace OpenRA if (commentStart >= 0 && !discardCommentsAndWhitespace) comment = line.Slice(commentStart); - // Remove leading/trailing whitespace guards if (value.Length > 1) { + // Remove leading/trailing whitespace guards var trimLeading = value[0] == '\\' && (value[1] == ' ' || value[1] == '\t') ? 1 : 0; var trimTrailing = value[value.Length - 1] == '\\' && (value[value.Length - 2] == ' ' || value[value.Length - 2] == '\t') ? 1 : 0; if (trimLeading + trimTrailing > 0) value = value.Slice(trimLeading, value.Length - trimLeading - trimTrailing); - } - // Remove escape characters from # - if (value.Contains("\\#", StringComparison.Ordinal)) - value = value.ToString().Replace("\\#", "#"); + // Remove escape characters from # + if (value.Contains("\\#", StringComparison.Ordinal)) + value = value.ToString().Replace("\\#", "#"); + } } - if (key != null || !discardCommentsAndWhitespace) + if (!key.IsEmpty || !discardCommentsAndWhitespace) { - var keyString = key == null ? null : key.ToString(); - var valueString = value == null ? null : value.ToString(); - var commentString = comment == null ? null : comment.ToString(); + var keyString = key.IsEmpty ? null : key.ToString(); + var valueString = value.IsEmpty ? null : value.ToString(); + + // Note: We need to support empty comments here to ensure that empty comments + // (i.e. a lone # at the end of a line) can be correctly re-serialized + var commentString = comment == default ? null : comment.ToString(); + keyString = keyString == null ? null : stringPool.GetOrAdd(keyString, keyString); valueString = valueString == null ? null : stringPool.GetOrAdd(valueString, valueString); commentString = commentString == null ? null : stringPool.GetOrAdd(commentString, commentString); diff --git a/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs b/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs index f26bddd223..5c3610db1a 100644 --- a/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs +++ b/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs @@ -198,17 +198,30 @@ Test: [TestCase(TestName = "Comments are correctly separated from values")] public void TestEscapedHashInValues() { - var trailingWhitespace = @"key: value # comment"; - Assert.AreEqual("value", MiniYaml.FromString(trailingWhitespace, "trailingWhitespace")[0].Value.Value); + var trailingWhitespace = MiniYaml.FromString(@"key: value # comment", "trailingWhitespace", discardCommentsAndWhitespace: false)[0]; + Assert.AreEqual("value", trailingWhitespace.Value.Value); + Assert.AreEqual(" comment", trailingWhitespace.Comment); - var noWhitespace = @"key:value# comment"; - Assert.AreEqual("value", MiniYaml.FromString(noWhitespace, "noWhitespace")[0].Value.Value); + var noWhitespace = MiniYaml.FromString(@"key:value# comment", "noWhitespace", discardCommentsAndWhitespace: false)[0]; + Assert.AreEqual("value", noWhitespace.Value.Value); + Assert.AreEqual(" comment", noWhitespace.Comment); - var escapedHashInValue = @"key: before \# after # comment"; - Assert.AreEqual("before # after", MiniYaml.FromString(escapedHashInValue, "escapedHashInValue")[0].Value.Value); + var escapedHashInValue = MiniYaml.FromString(@"key: before \# after # comment", "escapedHashInValue", discardCommentsAndWhitespace: false)[0]; + Assert.AreEqual("before # after", escapedHashInValue.Value.Value); + Assert.AreEqual(" comment", escapedHashInValue.Comment); - var emptyValue = @"key:# comment"; - Assert.AreEqual(null, MiniYaml.FromString(emptyValue, "emptyValue")[0].Value.Value); + var emptyValueAndComment = MiniYaml.FromString(@"key:#", "emptyValueAndComment", discardCommentsAndWhitespace: false)[0]; + Assert.AreEqual(null, emptyValueAndComment.Value.Value); + Assert.AreEqual("", emptyValueAndComment.Comment); + + var noValue = MiniYaml.FromString(@"key:", "noValue", discardCommentsAndWhitespace: false)[0]; + Assert.AreEqual(null, noValue.Value.Value); + Assert.AreEqual(null, noValue.Comment); + + var emptyKey = MiniYaml.FromString(@" : value", "emptyKey", discardCommentsAndWhitespace: false)[0]; + Assert.AreEqual(null, emptyKey.Key); + Assert.AreEqual("value", emptyKey.Value.Value); + Assert.AreEqual(null, emptyKey.Comment); } [TestCase(TestName = "Leading and trailing whitespace can be guarded using a backslash")]