From b8193154d7c3801ddf1589644963972c8938ed9c Mon Sep 17 00:00:00 2001 From: axunonb Date: Sat, 11 Oct 2025 16:46:20 +0200 Subject: [PATCH 1/3] fix: `IOutput.Write(...)` overloads now have argument `IFormattingInfo` set --- .../Core/Output/CustomOutputTests.cs | 53 +++++++++++++++++++ .../Core/Output/StringOutputTests.cs | 6 +-- .../Core/Formatting/FormattingInfo.cs | 4 +- src/SmartFormat/Core/Output/IOutput.cs | 7 ++- 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 src/SmartFormat.Tests/Core/Output/CustomOutputTests.cs diff --git a/src/SmartFormat.Tests/Core/Output/CustomOutputTests.cs b/src/SmartFormat.Tests/Core/Output/CustomOutputTests.cs new file mode 100644 index 00000000..36c406f8 --- /dev/null +++ b/src/SmartFormat.Tests/Core/Output/CustomOutputTests.cs @@ -0,0 +1,53 @@ +using System; +using System.Globalization; +using System.Text; +using NUnit.Framework; +using SmartFormat.Core.Extensions; +using SmartFormat.Core.Formatting; +using SmartFormat.Core.Output; +using SmartFormat.ZString; + +namespace SmartFormat.Tests.Core.Output; + +internal class CustomOutput : IOutput +{ + private readonly StringBuilder _sb = new(1000); + private const string OutputFormat = "Format: {0}, Selector {1}, Formatted: {2}"; + + public override string ToString() + { + return _sb.ToString(); + } + + public void Write(string text, IFormattingInfo? formattingInfo = null) + { + Write(text.AsSpan(), formattingInfo); + } + + public void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null) + { + var fi = (FormattingInfo) formattingInfo!; + _sb.AppendFormat(OutputFormat, fi.Format, fi.Selector, text.ToString()); + } + + public void Write(ZStringBuilder stringBuilder, IFormattingInfo? formattingInfo = null) + { + Write(stringBuilder.AsSpan(), formattingInfo); + } +} + +[TestFixture] +internal class CustomOutputTests +{ + [Test] + public void CustomOutput_GetsValid_FormattingInfo_Argument() + { + // This test ensures that the IOutput.Write method overloads + // get a valid IFormattingInfo argument. + var smart = Smart.CreateDefaultSmartFormat(); + var output = new CustomOutput(); + smart.FormatInto(output, CultureInfo.InvariantCulture, "{0:0.0000}", [9m]); + Assert.That(output.ToString(), + Is.EqualTo("Format: 0.0000, Selector 0, Formatted: 9.0000")); + } +} diff --git a/src/SmartFormat.Tests/Core/Output/StringOutputTests.cs b/src/SmartFormat.Tests/Core/Output/StringOutputTests.cs index baa15719..7a2c7bdc 100644 --- a/src/SmartFormat.Tests/Core/Output/StringOutputTests.cs +++ b/src/SmartFormat.Tests/Core/Output/StringOutputTests.cs @@ -12,7 +12,7 @@ public class StringOutputTests public void Output_Of_Span() { var so = new StringOutput(); - so.Write("text".AsSpan(), null!); + so.Write("text".AsSpan(), null); Assert.That(so.ToString(), Is.EqualTo("text")); } @@ -20,7 +20,7 @@ public void Output_Of_Span() public void Output_Of_String() { var so = new StringOutput(16); - so.Write("text", null!); + so.Write("text", null); Assert.That(so.ToString(), Is.EqualTo("text")); } @@ -30,7 +30,7 @@ public void Output_Of_ValueStringBuilder() var so = new StringOutput(); using var sb = ZStringBuilderUtilities.CreateZStringBuilder(); sb.Append("text"); - so.Write(sb, null!); + so.Write(sb, null); Assert.That(so.ToString(), Is.EqualTo("text")); } } diff --git a/src/SmartFormat/Core/Formatting/FormattingInfo.cs b/src/SmartFormat/Core/Formatting/FormattingInfo.cs index 4eafd42a..ae22a4d1 100644 --- a/src/SmartFormat/Core/Formatting/FormattingInfo.cs +++ b/src/SmartFormat/Core/Formatting/FormattingInfo.cs @@ -173,7 +173,7 @@ public void Write(ReadOnlySpan text) { if (Alignment == 0) { - FormatDetails.Output.Write(text); + FormatDetails.Output.Write(text, this); FormatDetails.Formatter.Evaluator.OnOutputWritten?.Invoke(this, new Evaluator.OutputWrittenEventArgs(text.ToString())); return; @@ -199,7 +199,7 @@ public void Write(ReadOnlySpan text) } // Write the aligned text to the output - FormatDetails.Output.Write(buffer.GetSpan()); + FormatDetails.Output.Write(buffer.GetSpan(), this); FormatDetails.Formatter.Evaluator.OnOutputWritten?.Invoke(this, new Evaluator.OutputWrittenEventArgs(buffer.ToString())); diff --git a/src/SmartFormat/Core/Output/IOutput.cs b/src/SmartFormat/Core/Output/IOutput.cs index 3766a150..4898b57b 100644 --- a/src/SmartFormat/Core/Output/IOutput.cs +++ b/src/SmartFormat/Core/Output/IOutput.cs @@ -18,6 +18,8 @@ public interface IOutput /// /// /// + // vNext: Remove formattingInfo argument? + [Obsolete("Use Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null) instead.", false)] void Write(string text, IFormattingInfo? formattingInfo = null); /// @@ -25,6 +27,7 @@ public interface IOutput /// /// /// + // vNext: Remove formattingInfo argument? void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null); /// @@ -32,5 +35,7 @@ public interface IOutput /// /// /// + // vNext: Remove formattingInfo argument? + [Obsolete("Use Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null) instead.", false)] void Write(ZStringBuilder stringBuilder, IFormattingInfo? formattingInfo = null); -} \ No newline at end of file +} From 4940f51c6d06321aaf5903a1688f623756c86ca2 Mon Sep 17 00:00:00 2001 From: axunonb Date: Sun, 12 Oct 2025 14:32:23 +0200 Subject: [PATCH 2/3] Make obsolete `IOutput.Write(...)` overload implementations call `Write(ReadOnlySpan, IFormattingInfo)` --- src/SmartFormat/Core/Output/IOutput.cs | 12 +++++++++--- src/SmartFormat/Core/Output/StringOutput.cs | 8 ++------ src/SmartFormat/Core/Output/TextWriterOutput.cs | 9 +++------ src/SmartFormat/Core/Output/ZStringOutput.cs | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/SmartFormat/Core/Output/IOutput.cs b/src/SmartFormat/Core/Output/IOutput.cs index 4898b57b..3d8c5cf0 100644 --- a/src/SmartFormat/Core/Output/IOutput.cs +++ b/src/SmartFormat/Core/Output/IOutput.cs @@ -16,9 +16,12 @@ public interface IOutput /// /// Writes a string to the output. /// + /// + /// Only implement a call to + /// using for ''. + /// /// /// - // vNext: Remove formattingInfo argument? [Obsolete("Use Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null) instead.", false)] void Write(string text, IFormattingInfo? formattingInfo = null); @@ -27,15 +30,18 @@ public interface IOutput /// /// /// - // vNext: Remove formattingInfo argument? + // vNext: Remove formattingInfo argument? Or make it non-optional and non-nullable? void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null); /// /// Writes text of a to the output. /// + /// + /// Only implement a call to + /// using for ''. + /// /// /// - // vNext: Remove formattingInfo argument? [Obsolete("Use Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null) instead.", false)] void Write(ZStringBuilder stringBuilder, IFormattingInfo? formattingInfo = null); } diff --git a/src/SmartFormat/Core/Output/StringOutput.cs b/src/SmartFormat/Core/Output/StringOutput.cs index 9f5371c2..0b9ebd78 100644 --- a/src/SmartFormat/Core/Output/StringOutput.cs +++ b/src/SmartFormat/Core/Output/StringOutput.cs @@ -57,7 +57,7 @@ public StringOutput(StringBuilder output) /// This parameter from will not be used here. public void Write(string text, IFormattingInfo? formattingInfo = null) { - Output.Append(text); + Write(text.AsSpan(), formattingInfo); } /// @@ -77,11 +77,7 @@ public void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = nul /// public void Write(ZStringBuilder stringBuilder, IFormattingInfo? formattingInfo = null) { -#if NETSTANDARD2_1 || NET6_0_OR_GREATER - Output.Append(stringBuilder.AsSpan()); -#else - Output.Append(stringBuilder); -#endif + Write(stringBuilder.AsSpan(), formattingInfo); } /// diff --git a/src/SmartFormat/Core/Output/TextWriterOutput.cs b/src/SmartFormat/Core/Output/TextWriterOutput.cs index 582dbbae..ffce9bcc 100644 --- a/src/SmartFormat/Core/Output/TextWriterOutput.cs +++ b/src/SmartFormat/Core/Output/TextWriterOutput.cs @@ -6,6 +6,7 @@ using System.IO; using SmartFormat.Core.Extensions; using SmartFormat.ZString; +using static System.Net.Mime.MediaTypeNames; namespace SmartFormat.Core.Output; @@ -31,7 +32,7 @@ public TextWriterOutput(TextWriter output) /// public void Write(string text, IFormattingInfo? formattingInfo = null) { - Output.Write(text); + Write(text.AsSpan(), formattingInfo); } /// @@ -47,10 +48,6 @@ public void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = nul /// public void Write(ZStringBuilder stringBuilder, IFormattingInfo? formattingInfo = null) { -#if NETSTANDARD2_1 || NET6_0_OR_GREATER - Output.Write(stringBuilder.AsSpan()); -#else - Output.Write(stringBuilder.ToString()); -#endif + Write(stringBuilder.AsSpan(), formattingInfo); } } diff --git a/src/SmartFormat/Core/Output/ZStringOutput.cs b/src/SmartFormat/Core/Output/ZStringOutput.cs index ffecf586..48d57180 100644 --- a/src/SmartFormat/Core/Output/ZStringOutput.cs +++ b/src/SmartFormat/Core/Output/ZStringOutput.cs @@ -60,7 +60,7 @@ public ZStringOutput(ZStringBuilder stringBuilder) /// public void Write(string text, IFormattingInfo? formattingInfo = null) { - Output.Append(text); + Write(text.AsSpan(), formattingInfo); } /// @@ -72,7 +72,7 @@ public void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = nul /// public void Write(ZStringBuilder stringBuilder, IFormattingInfo? formattingInfo = null) { - Output.Append(stringBuilder); + Write(stringBuilder.AsSpan(), formattingInfo); } /// From 6928bf25b63c209ec6861e343434cbbba23c34dd Mon Sep 17 00:00:00 2001 From: axunonb Date: Mon, 13 Oct 2025 23:56:11 +0200 Subject: [PATCH 3/3] Fixes from review --- src/SmartFormat/Core/Output/IOutput.cs | 2 +- src/SmartFormat/Core/Output/TextWriterOutput.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SmartFormat/Core/Output/IOutput.cs b/src/SmartFormat/Core/Output/IOutput.cs index 3d8c5cf0..bfdff630 100644 --- a/src/SmartFormat/Core/Output/IOutput.cs +++ b/src/SmartFormat/Core/Output/IOutput.cs @@ -30,7 +30,7 @@ public interface IOutput /// /// /// - // vNext: Remove formattingInfo argument? Or make it non-optional and non-nullable? + // v4: Remove formattingInfo argument? Or make it non-optional and non-nullable? void Write(ReadOnlySpan text, IFormattingInfo? formattingInfo = null); /// diff --git a/src/SmartFormat/Core/Output/TextWriterOutput.cs b/src/SmartFormat/Core/Output/TextWriterOutput.cs index ffce9bcc..d51bb7b6 100644 --- a/src/SmartFormat/Core/Output/TextWriterOutput.cs +++ b/src/SmartFormat/Core/Output/TextWriterOutput.cs @@ -6,7 +6,6 @@ using System.IO; using SmartFormat.Core.Extensions; using SmartFormat.ZString; -using static System.Net.Mime.MediaTypeNames; namespace SmartFormat.Core.Output;