diff --git a/src/SharpYaml.Tests/EmitterTests.cs b/src/SharpYaml.Tests/EmitterTests.cs index 179e615..47a0065 100644 --- a/src/SharpYaml.Tests/EmitterTests.cs +++ b/src/SharpYaml.Tests/EmitterTests.cs @@ -43,6 +43,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -306,5 +307,46 @@ public void FoldedStylePreservesNewLines() Dump.WriteLine(output); Assert.AreEqual(input, output); } + + [Test] + public void FoldedScalarWithMultipleWordsPreservesLineBreaks() + { + // The real issue is not that "a folded\nscalar" should become "a folded scalar" + // in terms of content (that's actually correct YAML behavior) + // The issue is that when emitting a scalar with newlines as a folded scalar, + // it should preserve the newlines in the YAML structure + + var input = "a folded\nscalar"; + + // When we emit a scalar with embedded newlines as a folded scalar, + // it should be emitted as: + // >- + // a folded + // scalar + // NOT as: + // >- + // a folded scalar + + var yaml = EmitScalar(new Scalar(null, null, input, ScalarStyle.Folded, true, false)); + Console.WriteLine("Emitted YAML:"); + Console.WriteLine(yaml); + + // The emitted YAML should contain the folded scalar structure + Assert.That(yaml, Does.Contain(">-"), "Should emit as folded scalar"); + Assert.That(yaml, Does.Contain("a folded"), "Should contain the first part"); + Assert.That(yaml, Does.Contain("scalar"), "Should contain the second part"); + + // Parse it back and verify the content is preserved + var stream = new YamlStream(); + stream.Load(new StringReader(yaml)); + var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; + var scalar = (YamlScalarNode)sequence.Children[0]; + + Console.WriteLine($"Original: '{input}'"); + Console.WriteLine($"Round-trip result: '{scalar.Value}'"); + + // This should pass - the content should be preserved + Assert.AreEqual(input, scalar.Value, "Folded scalar content should be preserved during round-trip"); + } } } diff --git a/src/SharpYaml/Emitter.cs b/src/SharpYaml/Emitter.cs index cd302c2..11d2640 100644 --- a/src/SharpYaml/Emitter.cs +++ b/src/SharpYaml/Emitter.cs @@ -546,7 +546,7 @@ private void AnalyzeScalar(string value) scalarData.isSingleQuotedAllowed = false; } - if (space_break || special_characters) + if (space_break) { scalarData.isFlowPlainAllowed = false; scalarData.isBlockPlainAllowed = false; @@ -554,6 +554,20 @@ private void AnalyzeScalar(string value) scalarData.isBlockAllowed = false; } + if (special_characters) + { + scalarData.isFlowPlainAllowed = false; + scalarData.isBlockPlainAllowed = false; + scalarData.isSingleQuotedAllowed = false; + // Don't disable block scalars for line breaks - they're the point of folded/literal scalars + // However, disable block scalars for single-character strings containing only special characters + // as they're better represented with quoted styles + if (!line_breaks || (line_breaks && value.Length == 1)) + { + scalarData.isBlockAllowed = false; + } + } + if (line_breaks) { scalarData.isFlowPlainAllowed = false; @@ -1520,10 +1534,25 @@ private void SelectScalarStyle(ParsingEvent evt) if (style == ScalarStyle.Literal || style == ScalarStyle.Folded) { - if (!scalarData.isBlockAllowed || flowLevel != 0 || isSimpleKeyContext) + // Only override block styles if they're truly not possible to emit + // Don't override if the user explicitly requested Folded/Literal style + // unless we're in a context where block scalars are impossible (flow context, simple key) + if (flowLevel != 0 || isSimpleKeyContext) { style = ScalarStyle.DoubleQuoted; } + // For both literal and folded scalars, fall back to double quotes if block scalars aren't allowed + else if (!scalarData.isBlockAllowed) + { + style = ScalarStyle.DoubleQuoted; + } + } + + // Final fallback: if no style is allowed, always use double quoted + if (!scalarData.isFlowPlainAllowed && !scalarData.isBlockPlainAllowed && + !scalarData.isSingleQuotedAllowed && !scalarData.isBlockAllowed) + { + style = ScalarStyle.DoubleQuoted; } // TODO: What is this code supposed to mean? @@ -1532,6 +1561,13 @@ private void SelectScalarStyle(ParsingEvent evt) // tagData.handle = "!"; //} + // Final fallback: if no style is allowed, always use double quoted + if (!scalarData.isFlowPlainAllowed && !scalarData.isBlockPlainAllowed && + !scalarData.isSingleQuotedAllowed && !scalarData.isBlockAllowed) + { + style = ScalarStyle.DoubleQuoted; + } + scalarData.style = style; } @@ -1545,7 +1581,7 @@ private void EmitAlias() } /// - /// Write an achor. + /// Write an anchor. /// private void ProcessAnchor() { diff --git a/src/SharpYaml/Serialization/Serializers/PrimitiveSerializer.cs b/src/SharpYaml/Serialization/Serializers/PrimitiveSerializer.cs index 597fa1a..9e92a7e 100644 --- a/src/SharpYaml/Serialization/Serializers/PrimitiveSerializer.cs +++ b/src/SharpYaml/Serialization/Serializers/PrimitiveSerializer.cs @@ -126,7 +126,7 @@ internal class PrimitiveSerializer : ScalarSerializerBase, IYamlSerializableFact { throw new YamlException(scalar.Start, scalar.End, $"Unable to decode char from [{text}]. Expecting a string of length == 1"); } - return text.ToCharArray()[0]; + return text[0]; case TypeCode.Byte: return byte.Parse(text, CultureInfo.InvariantCulture); case TypeCode.SByte: