Skip to content

Commit bd054ef

Browse files
authored
feat/scalar line breaks (#124)
* tests: adds a unit test for line breaks in scalars Signed-off-by: Vincent Biret <vincentbiret@hotmail.com> * perf: removes redundant call to char array Signed-off-by: Vincent Biret <vincentbiret@hotmail.com> * fix: scalars now maintain line returns Signed-off-by: Vincent Biret <vincentbiret@hotmail.com> fix: allow literals to be on multiple lines Signed-off-by: Vincent Biret <vincentbiret@hotmail.com> --------- Signed-off-by: Vincent Biret <vincentbiret@hotmail.com>
1 parent c90fe44 commit bd054ef

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

src/SharpYaml.Tests/EmitterTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4444
// SOFTWARE.
4545

46+
using System;
4647
using System.Collections;
4748
using System.Collections.Generic;
4849
using System.IO;
@@ -306,5 +307,46 @@ public void FoldedStylePreservesNewLines()
306307
Dump.WriteLine(output);
307308
Assert.AreEqual(input, output);
308309
}
310+
311+
[Test]
312+
public void FoldedScalarWithMultipleWordsPreservesLineBreaks()
313+
{
314+
// The real issue is not that "a folded\nscalar" should become "a folded scalar"
315+
// in terms of content (that's actually correct YAML behavior)
316+
// The issue is that when emitting a scalar with newlines as a folded scalar,
317+
// it should preserve the newlines in the YAML structure
318+
319+
var input = "a folded\nscalar";
320+
321+
// When we emit a scalar with embedded newlines as a folded scalar,
322+
// it should be emitted as:
323+
// >-
324+
// a folded
325+
// scalar
326+
// NOT as:
327+
// >-
328+
// a folded scalar
329+
330+
var yaml = EmitScalar(new Scalar(null, null, input, ScalarStyle.Folded, true, false));
331+
Console.WriteLine("Emitted YAML:");
332+
Console.WriteLine(yaml);
333+
334+
// The emitted YAML should contain the folded scalar structure
335+
Assert.That(yaml, Does.Contain(">-"), "Should emit as folded scalar");
336+
Assert.That(yaml, Does.Contain("a folded"), "Should contain the first part");
337+
Assert.That(yaml, Does.Contain("scalar"), "Should contain the second part");
338+
339+
// Parse it back and verify the content is preserved
340+
var stream = new YamlStream();
341+
stream.Load(new StringReader(yaml));
342+
var sequence = (YamlSequenceNode)stream.Documents[0].RootNode;
343+
var scalar = (YamlScalarNode)sequence.Children[0];
344+
345+
Console.WriteLine($"Original: '{input}'");
346+
Console.WriteLine($"Round-trip result: '{scalar.Value}'");
347+
348+
// This should pass - the content should be preserved
349+
Assert.AreEqual(input, scalar.Value, "Folded scalar content should be preserved during round-trip");
350+
}
309351
}
310352
}

src/SharpYaml/Emitter.cs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,14 +546,28 @@ private void AnalyzeScalar(string value)
546546
scalarData.isSingleQuotedAllowed = false;
547547
}
548548

549-
if (space_break || special_characters)
549+
if (space_break)
550550
{
551551
scalarData.isFlowPlainAllowed = false;
552552
scalarData.isBlockPlainAllowed = false;
553553
scalarData.isSingleQuotedAllowed = false;
554554
scalarData.isBlockAllowed = false;
555555
}
556556

557+
if (special_characters)
558+
{
559+
scalarData.isFlowPlainAllowed = false;
560+
scalarData.isBlockPlainAllowed = false;
561+
scalarData.isSingleQuotedAllowed = false;
562+
// Don't disable block scalars for line breaks - they're the point of folded/literal scalars
563+
// However, disable block scalars for single-character strings containing only special characters
564+
// as they're better represented with quoted styles
565+
if (!line_breaks || (line_breaks && value.Length == 1))
566+
{
567+
scalarData.isBlockAllowed = false;
568+
}
569+
}
570+
557571
if (line_breaks)
558572
{
559573
scalarData.isFlowPlainAllowed = false;
@@ -1520,10 +1534,25 @@ private void SelectScalarStyle(ParsingEvent evt)
15201534

15211535
if (style == ScalarStyle.Literal || style == ScalarStyle.Folded)
15221536
{
1523-
if (!scalarData.isBlockAllowed || flowLevel != 0 || isSimpleKeyContext)
1537+
// Only override block styles if they're truly not possible to emit
1538+
// Don't override if the user explicitly requested Folded/Literal style
1539+
// unless we're in a context where block scalars are impossible (flow context, simple key)
1540+
if (flowLevel != 0 || isSimpleKeyContext)
15241541
{
15251542
style = ScalarStyle.DoubleQuoted;
15261543
}
1544+
// For both literal and folded scalars, fall back to double quotes if block scalars aren't allowed
1545+
else if (!scalarData.isBlockAllowed)
1546+
{
1547+
style = ScalarStyle.DoubleQuoted;
1548+
}
1549+
}
1550+
1551+
// Final fallback: if no style is allowed, always use double quoted
1552+
if (!scalarData.isFlowPlainAllowed && !scalarData.isBlockPlainAllowed &&
1553+
!scalarData.isSingleQuotedAllowed && !scalarData.isBlockAllowed)
1554+
{
1555+
style = ScalarStyle.DoubleQuoted;
15271556
}
15281557

15291558
// TODO: What is this code supposed to mean?
@@ -1532,6 +1561,13 @@ private void SelectScalarStyle(ParsingEvent evt)
15321561
// tagData.handle = "!";
15331562
//}
15341563

1564+
// Final fallback: if no style is allowed, always use double quoted
1565+
if (!scalarData.isFlowPlainAllowed && !scalarData.isBlockPlainAllowed &&
1566+
!scalarData.isSingleQuotedAllowed && !scalarData.isBlockAllowed)
1567+
{
1568+
style = ScalarStyle.DoubleQuoted;
1569+
}
1570+
15351571
scalarData.style = style;
15361572
}
15371573

@@ -1545,7 +1581,7 @@ private void EmitAlias()
15451581
}
15461582

15471583
/// <summary>
1548-
/// Write an achor.
1584+
/// Write an anchor.
15491585
/// </summary>
15501586
private void ProcessAnchor()
15511587
{

src/SharpYaml/Serialization/Serializers/PrimitiveSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ internal class PrimitiveSerializer : ScalarSerializerBase, IYamlSerializableFact
126126
{
127127
throw new YamlException(scalar.Start, scalar.End, $"Unable to decode char from [{text}]. Expecting a string of length == 1");
128128
}
129-
return text.ToCharArray()[0];
129+
return text[0];
130130
case TypeCode.Byte:
131131
return byte.Parse(text, CultureInfo.InvariantCulture);
132132
case TypeCode.SByte:

0 commit comments

Comments
 (0)