diff --git a/src/SmartFormat.Performance/SimpleSpanParserTests.cs b/src/SmartFormat.Performance/SimpleSpanParserTests.cs index d38178ed..392201dc 100644 --- a/src/SmartFormat.Performance/SimpleSpanParserTests.cs +++ b/src/SmartFormat.Performance/SimpleSpanParserTests.cs @@ -60,7 +60,7 @@ public void Setup() [Benchmark] public void ParseSmartFormat() { - var result = _sfParser.ParseFormat(_inputFormatString, new[] {"default"}); + var result = _sfParser.ParseFormat(_inputFormatString); } /// diff --git a/src/SmartFormat.Performance/SourcePerformanceTests.cs b/src/SmartFormat.Performance/SourcePerformanceTests.cs index a90b6fe2..9ae981ef 100644 --- a/src/SmartFormat.Performance/SourcePerformanceTests.cs +++ b/src/SmartFormat.Performance/SourcePerformanceTests.cs @@ -109,11 +109,10 @@ public SourcePerformanceTests() ); // Cache the parsing result, so we don't include parsing performance - var format = _jsonFormatter.Parser.ParseFormat(_format, _jsonFormatter.FormatterExtensions[0].Names); + var format = _jsonFormatter.Parser.ParseFormat(_format); _formatCache = new FormatCache(format); - var formatForLiteral = _jsonFormatter.Parser.ParseFormat(_formatForLiteral, - _jsonFormatter.FormatterExtensions[0].Names); + var formatForLiteral = _jsonFormatter.Parser.ParseFormat(_formatForLiteral); _formatCacheLiteral = new FormatCache(formatForLiteral); } diff --git a/src/SmartFormat.Tests/Core/FormatCacheTests.cs b/src/SmartFormat.Tests/Core/FormatCacheTests.cs index b5da3c81..a4218902 100644 --- a/src/SmartFormat.Tests/Core/FormatCacheTests.cs +++ b/src/SmartFormat.Tests/Core/FormatCacheTests.cs @@ -38,7 +38,7 @@ public void Format_WithCache() var data = new {Name = "Joe", City = "Melbourne"}; var formatter = GetSimpleFormatter(); var formatString = "{Name}, {City}"; - var format = formatter.Parser.ParseFormat(formatString, formatter.FormatterExtensions[0].Names); + var format = formatter.Parser.ParseFormat(formatString); var cache = new FormatCache(format); Assert.That(formatter.FormatWithCache(ref cache, formatString, data), Is.EqualTo($"{data.Name}, {data.City}")); } @@ -49,7 +49,7 @@ public void Format_WithCache_Into() var data = new {Name = "Joe", City = "Melbourne"}; var formatter = GetSimpleFormatter(); var formatString = "{Name}, {City}"; - var format = formatter.Parser.ParseFormat(formatString, formatter.FormatterExtensions[0].Names); + var format = formatter.Parser.ParseFormat(formatString); var cache = new FormatCache(format); var output = new StringOutput(); formatter.FormatWithCacheInto(ref cache, output, formatString, data); diff --git a/src/SmartFormat.Tests/Core/FormatterTests.cs b/src/SmartFormat.Tests/Core/FormatterTests.cs index 34e50bd3..a7eb1aa3 100644 --- a/src/SmartFormat.Tests/Core/FormatterTests.cs +++ b/src/SmartFormat.Tests/Core/FormatterTests.cs @@ -109,17 +109,24 @@ public void Nested_Placeholders_Braces() var data = new {Person = new {FirstName = "John", LastName = "Long"}, Address = new {City = "London"}}; var formatter = Smart.CreateDefaultSmartFormat(); - // This is necessary to avoid undesired trailing blanks: - // }}} are now considered as 3 different closing braces - formatter.Parser.UseAlternativeEscapeChar('\\'); - // This allows a nested template to access outer scopes. // Here, {City} will come from Address, but {FirstName} will come from Person: - var result = formatter.Format("{Person:{Address:City: {City}, Name: {FirstName}}}", data); + var result = formatter.Format("{Person:{Address:City\\: {City}, Name\\: {FirstName}}}", data); Assert.That(result, Is.EqualTo("City: London, Name: John")); } + [TestCase("({.Joe.})", ":{Joe}:")] + [TestCase("Kate", ":{(.Not:Joe.)}:")] + public void Any_Character_Anywhere_If_Escaped(string name, string expected) + { + var smart = Smart.CreateDefaultSmartFormat(); + var arg = new {Name = name}; + // {} and () must and can only be escaped inside options + var format = @":\{{Name:choose(\(\{.Joe.\}\)):Joe|(.Not\:Joe.)}\}:"; + Assert.That(smart.Format(format, arg), Is.EqualTo(expected)); + } + [TestCase(1)] [TestCase(2)] public void Nested_PlaceHolders_Conditional(int numOfPeople) @@ -153,7 +160,7 @@ public void LeadingBackslashMustNotEscapeBraces() { var smart = Smart.CreateDefaultSmartFormat(); smart.Settings.Parser.ConvertCharacterStringLiterals = false; - smart.Settings.Parser.UseStringFormatCompatibility = true; + smart.Settings.UseStringFormatCompatibility = true; var expected = "\\Hello"; var actual = smart.Format("\\{Test}", new { Test = "Hello" }); @@ -187,7 +194,7 @@ public void SmartFormatter_FormatDetails() formatter.Settings.Formatter.ErrorAction = FormatErrorAction.OutputErrorInResult; formatter.Settings.Parser.ErrorAction = ParseErrorAction.OutputErrorInResult; formatter.Parser.AddAlphanumericSelectors(); // required for this test - var formatParsed = formatter.Parser.ParseFormat(format, new []{string.Empty}); + var formatParsed = formatter.Parser.ParseFormat(format); var formatDetails = new FormatDetails(formatter, formatParsed, args, null, null, output); Assert.AreEqual(args, formatDetails.OriginalArgs); diff --git a/src/SmartFormat.Tests/Core/ParserTests.cs b/src/SmartFormat.Tests/Core/ParserTests.cs index 49cafc5f..50c7c6a6 100644 --- a/src/SmartFormat.Tests/Core/ParserTests.cs +++ b/src/SmartFormat.Tests/Core/ParserTests.cs @@ -27,7 +27,7 @@ public void TestParser() "{a}", " aaa {bbb_bbb.CCC} ddd ", }; - var results = formats.Select(f => new { format = f, parsed = parser.ParseFormat(f, new[] { Guid.NewGuid().ToString("N") }) }).ToArray(); + var results = formats.Select(f => new { format = f, parsed = parser.ParseFormat(f) }).ToArray(); // Verify that the reconstructed formats // match the original ones: @@ -106,7 +106,7 @@ public void Parser_Ignores_Exceptions() }; foreach (var format in invalidFormats) { - _ = parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") }); + _ = parser.ParseFormat(format); } } @@ -121,7 +121,7 @@ public void Parser_Error_Action_Ignore() var parser = GetRegularParser(); parser.Settings.Parser.ErrorAction = ParseErrorAction.Ignore; - var parsed = parser.ParseFormat(invalidTemplate, new[] { Guid.NewGuid().ToString("N") }); + var parsed = parser.ParseFormat(invalidTemplate); Assert.That(parsed.Items.Count, Is.EqualTo(4), "Number of parsed items"); Assert.That(parsed.Items[0].RawText, Is.EqualTo("Hello, I'm "), "Literal text"); @@ -141,7 +141,7 @@ public void Parser_Error_Action_MaintainTokens(string invalidTemplate, bool last { var parser = GetRegularParser(); parser.Settings.Parser.ErrorAction = ParseErrorAction.MaintainTokens; - var parsed = parser.ParseFormat(invalidTemplate, new[] { Guid.NewGuid().ToString("N") }); + var parsed = parser.ParseFormat(invalidTemplate); Assert.That(parsed.Items.Count, Is.EqualTo(4), "Number of parsed items"); Assert.That(parsed.Items[0].RawText, Is.EqualTo("Hello, I'm ")); @@ -167,7 +167,7 @@ public void Parser_Error_Action_OutputErrorInResult() var parser = GetRegularParser(); parser.Settings.Parser.ErrorAction = ParseErrorAction.OutputErrorInResult; - var parsed = parser.ParseFormat(invalidTemplate, new[] { Guid.NewGuid().ToString("N") }); + var parsed = parser.ParseFormat(invalidTemplate); Assert.That(parsed.Items.Count, Is.EqualTo(1)); Assert.That(parsed.Items[0].RawText, Does.StartWith("The format string has 3 issues")); @@ -241,7 +241,7 @@ function interpolationSearch(sortedArray, seekIndex) { "; var parser = GetRegularParser(); parser.Settings.Parser.ErrorAction = ParseErrorAction.MaintainTokens; - var parsed = parser.ParseFormat(js, new[] { Guid.NewGuid().ToString("N") }); + var parsed = parser.ParseFormat(js); // No characters should get lost compared to the format string, // no matter if a Placeholder can be identified or not @@ -290,7 +290,7 @@ .comment img { "; var parser = GetRegularParser(); parser.Settings.Parser.ErrorAction = ParseErrorAction.MaintainTokens; - var parsed = parser.ParseFormat(css, new[] { Guid.NewGuid().ToString("N") }); + var parsed = parser.ParseFormat(css); // No characters should get lost compared to the format string, // no matter if a Placeholder can be identified or not @@ -313,8 +313,6 @@ .comment img { private static Parser GetRegularParser() { var parser = new SmartFormatter() { Settings = { Parser = {ErrorAction = ParseErrorAction.ThrowError }}}.Parser; - parser.AddAlphanumericSelectors(); - parser.AddOperators(".,"); return parser; } @@ -324,7 +322,7 @@ public void Test_Format_Substring() var parser = GetRegularParser(); var formatString = " a|aa {bbb: ccc dd|d {:|||} {eee} ff|f } gg|g "; - var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") }); + var format = parser.ParseFormat(formatString); // Extract the substrings of literal text: Assert.That(format.Substring( 1, 3).ToString(), Is.EqualTo("a|a")); @@ -364,7 +362,7 @@ public void Test_Format_Set_Alignment_Property() var parser = GetRegularParser(); var formatString = "{0}"; - var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") }); + var format = parser.ParseFormat(formatString); var placeholder = (Placeholder) format.Items[0]; Assert.AreEqual(formatString, placeholder.ToString()); placeholder.Alignment = 10; @@ -377,7 +375,7 @@ public void Test_Format_With_Alignment() var parser = GetRegularParser(); var formatString = "{0,-10}"; - var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") }); + var format = parser.ParseFormat(formatString); var placeholder = (Placeholder) format.Items[0]; Assert.That(placeholder.ToString(), Is.EqualTo(formatString)); Assert.That(placeholder.Selectors.Count, Is.EqualTo(2)); @@ -391,7 +389,7 @@ public void Test_Formatter_Name_And_Options() var parser = GetRegularParser(); var formatString = "{0}"; - var format = parser.ParseFormat(formatString, new[] { Guid.NewGuid().ToString("N") }); + var format = parser.ParseFormat(formatString); var placeholder = (Placeholder)format.Items[0]; placeholder.FormatterName = "test"; Assert.AreEqual($"{{0:{placeholder.FormatterName}}}", placeholder.ToString()); @@ -404,7 +402,7 @@ public void Test_Format_IndexOf() { var parser = GetRegularParser(); var format = " a|aa {bbb: ccc dd|d {:|||} {eee} ff|f } gg|g "; - var Format = parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") }); + var Format = parser.ParseFormat(format); Assert.That(Format.IndexOf('|'), Is.EqualTo(2)); Assert.That(Format.IndexOf('|', 3), Is.EqualTo(43)); @@ -427,7 +425,7 @@ public void Test_Format_Split() { var parser = GetRegularParser(); var format = " a|aa {bbb: ccc dd|d {:|||} {eee} ff|f } gg|g "; - var parsedFormat = parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") }); + var parsedFormat = parser.ParseFormat(format); var splits = parsedFormat.Split('|'); Assert.That(splits.Count, Is.EqualTo(3)); @@ -449,7 +447,7 @@ public void Test_Format_Split() private Format Parse(string format, string[] formatterExentionNames ) { - return GetRegularParser().ParseFormat(format, formatterExentionNames); + return GetRegularParser().ParseFormat(format); } [TestCase("{0:name:}", "name", "", "")] @@ -478,7 +476,7 @@ public void Name_of_unregistered_NamedFormatter_will_not_be_parsed() { // find formatter formattername, which does not exist in the (empty) list of formatter extensions var placeholderWithNonExistingName = (Placeholder)Parse("{0:formattername:}", new string[] {} ).Items[0]; - Assert.AreEqual("formattername:", placeholderWithNonExistingName.Format?.ToString()); // name is only treaded as a literal + Assert.AreEqual("formattername", placeholderWithNonExistingName.FormatterName); // name is only treaded as a literal } // Incomplete: @@ -528,7 +526,7 @@ private void AssertNoNamedFormatter(string format, string expectedLiteralText) parser.UseAlternativeEscapeChar('\\'); parser.Settings.ConvertCharacterStringLiterals = false; - var placeholder = (Placeholder) parser.ParseFormat(format, new[] { Guid.NewGuid().ToString("N") }).Items[0]; + var placeholder = (Placeholder) parser.ParseFormat(format).Items[0]; Assert.IsEmpty(placeholder.FormatterName); Assert.IsEmpty(placeholder.FormatterOptions); var literalText = placeholder.Format?.GetLiteralText(); @@ -553,7 +551,7 @@ public void Alternative_Escaping_In_Literal() { var parser = GetRegularParser(); parser.UseAlternativeEscapeChar('\\'); - Assert.That(parser.ParseFormat("\\{\\}", Array.Empty()).ToString(), Is.EqualTo("{}")); + Assert.That(parser.ParseFormat("\\{\\}").ToString(), Is.EqualTo("{}")); } [Test] @@ -562,7 +560,7 @@ public void Nested_format_with_alternative_escaping() var parser = GetRegularParser(); // necessary because of the consecutive }}}, which would otherwise be escaped as }} and lead to "missing brace" exception: parser.UseAlternativeEscapeChar('\\'); - var placeholders = parser.ParseFormat("{c1:{c2:{c3}}}", new[] {Guid.NewGuid().ToString("N")}); + var placeholders = parser.ParseFormat("{c1:{c2:{c3}}}"); var c1 = (Placeholder) placeholders.Items[0]; var c2 = (Placeholder) c1.Format?.Items[0]!; @@ -585,7 +583,7 @@ public void Parse_Options() // The literal may also contain escaped characters var literal = "one|two|th\\} \\{ree|other"; - var format = parser.ParseFormat($"{{{selector}:{formatterName}({options}):{literal}}}", new[] {formatterName}); + var format = parser.ParseFormat($"{{{selector}:{formatterName}({options}):{literal}}}"); var placeholder = (Placeholder) format.Items[0]; Assert.That(format.Items.Count, Is.EqualTo(1)); @@ -606,7 +604,7 @@ public void Parse_Options() public void Parse_Unicode(string formatString, string unicodeLiteral, int itemIndex, bool isLegal) { var parser = GetRegularParser(); - var result = parser.ParseFormat(formatString, new[] {"d"}); + var result = parser.ParseFormat(formatString); var literal = result.Items[itemIndex]; Assert.That(literal, Is.TypeOf(typeof(LiteralText))); @@ -625,7 +623,7 @@ public void Selector_With_Custom_Selector_Character(string formatString, char cu { var parser = GetRegularParser(); parser.Settings.Parser.AddCustomSelectorChars(new[]{customChar}); - var result = parser.ParseFormat(formatString, new[] {"d"}); + var result = parser.ParseFormat(formatString); var placeholder = result.Items[0] as Placeholder; Assert.That(placeholder, Is.Not.Null); @@ -639,7 +637,7 @@ public void Selectors_With_Custom_Operator_Character(string formatString, char c { var parser = GetRegularParser(); parser.Settings.Parser.AddCustomOperatorChars(new[]{customChar}); - var result = parser.ParseFormat(formatString, new[] {"d"}); + var result = parser.ParseFormat(formatString); var placeholder = result.Items[0] as Placeholder; Assert.That(placeholder, Is.Not.Null); @@ -658,7 +656,7 @@ public void Selector_With_Contiguous_Operator_Characters(string formatString, ch var parser = GetRegularParser(); // adding '.' is ignored, as it's the standard operator parser.Settings.Parser.AddCustomOperatorChars(new[]{customChar}); - var result = parser.ParseFormat(formatString, new[] {"d"}); + var result = parser.ParseFormat(formatString); var placeholder = result.Items[0] as Placeholder; Assert.That(placeholder, Is.Not.Null); diff --git a/src/SmartFormat.Tests/Core/StringFormatCompatibilityTests.cs b/src/SmartFormat.Tests/Core/StringFormatCompatibilityTests.cs index e5217db4..18635cc6 100644 --- a/src/SmartFormat.Tests/Core/StringFormatCompatibilityTests.cs +++ b/src/SmartFormat.Tests/Core/StringFormatCompatibilityTests.cs @@ -2,17 +2,22 @@ using System.Collections.Generic; using System.Globalization; using NUnit.Framework; -using SmartFormat.Core.Formatting; -using SmartFormat.Core.Parsing; -using SmartFormat.Core.Settings; -using SmartFormat.Tests.TestUtils; -using SmartFormat.Utilities; namespace SmartFormat.Tests.Core { + /// + /// These tests run with string.Format compatibility switched ON + /// [TestFixture] public class StringFormatCompatibilityTests { + private static SmartFormatter _formatter = Smart.CreateDefaultSmartFormat(); + + [SetUp] + public void Setup() + { + _formatter.Settings.UseStringFormatCompatibility = true; + } [Test] public void IndexPlaceholderDecimal() @@ -21,8 +26,8 @@ public void IndexPlaceholderDecimal() var cultureDE = new CultureInfo("de-DE"); var fmt = "Today's temperature is {0}°C."; var temp = 20.45m; - Assert.AreEqual(string.Format(cultureUS, fmt, temp), Smart.Format(cultureUS, fmt, temp)); - Assert.AreEqual(string.Format(cultureDE, fmt, temp), Smart.Format(cultureDE, fmt, temp)); + Assert.That(_formatter.Format(cultureUS, fmt, temp), Is.EqualTo(string.Format(cultureUS, fmt, temp))); + Assert.That(_formatter.Format(cultureDE, fmt, temp), Is.EqualTo(string.Format(cultureDE, fmt, temp))); } [Test] @@ -32,36 +37,45 @@ public void IndexPlaceholderDateTime() var cultureDE = new CultureInfo("de-DE"); var fmt = "It is now {0:d} at {0:t}"; var now = DateTime.Now; - Assert.AreEqual(string.Format(cultureUS, fmt, now), Smart.Format(cultureUS, fmt, now)); - Assert.AreEqual(string.Format(cultureDE, fmt, now), Smart.Format(cultureDE, fmt, now)); + Assert.That(_formatter.Format(cultureUS, fmt, now), Is.EqualTo(string.Format(cultureUS, fmt, now))); + Assert.That(_formatter.Format(cultureDE, fmt, now), Is.EqualTo(string.Format(cultureDE, fmt, now))); } [Test] public void IndexPlaceholderDateTimeHHmmss() { // columns in the time part must not be recognized as delimiters of a named placeholder - // (except a formatter's name would really be 'yyyy/MM/dd HH') var fmt = "It is now {0:yyyy/MM/dd HH:mm:ss}"; var now = DateTime.Now; - Assert.AreEqual(string.Format(fmt, now), Smart.Format(fmt, now)); + Assert.That(_formatter.Format(fmt, now), Is.EqualTo(string.Format(fmt, now))); + } + + [Test] + public void NamedPlaceholderDateTimeHHmmss() + { + // columns in the time part must not be recognized as delimiters of a named placeholder + var now = DateTime.Now; + var smartFmt = "It is now {Date:yyyy/MM/dd HH:mm:ss}"; + var stringFmt = $"It is now {now.Date:yyyy/MM/dd HH:mm:ss}"; + Assert.That(_formatter.Format(smartFmt, now), Is.EqualTo(stringFmt)); + } [Test] public void IndexPlaceholderAlignment() { - // columns in the time part must not be recogniced as delimiters of a named placeholder - // (except a formatter's name would really be 'yyyy/MM/dd HH') + // columns in the time part must not be recognized as delimiters of a named placeholder var fmt = "Year: {0,-6} Amount: {1,15:N0}"; var year = 2017; var amount = 1025632; - Assert.AreEqual(string.Format(fmt, year, amount), Smart.Format(fmt, year, amount)); + Assert.That(_formatter.Format(fmt, year, amount), Is.EqualTo(string.Format(fmt, year, amount))); } [Test] public void SmartFormat_With_Three_Arguments() { var args = new Dictionary { {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}}; - Assert.AreEqual($"{args["key1"]} {args["key2"]} {args["key3"]}", Smart.Format("{0} {1} {2}", args["key1"], args["key2"], args["key3"])); + Assert.That(_formatter.Format("{0} {1} {2}", args["key1"], args["key2"], args["key3"]), Is.EqualTo($"{args["key1"]} {args["key2"]} {args["key3"]}")); } [Test] @@ -71,8 +85,8 @@ public void NamedPlaceholderDecimal() var cultureDE = new CultureInfo("de-DE"); var fmt = "Today's temperature is {0}°C."; var temp = 20.45m; - Assert.AreEqual(string.Format(cultureUS, fmt, temp), Smart.Format(cultureUS, fmt, temp)); - Assert.AreEqual(string.Format(cultureDE, fmt, temp), Smart.Format(cultureDE, fmt, temp)); + Assert.That(_formatter.Format(cultureUS, fmt, temp), Is.EqualTo(string.Format(cultureUS, fmt, temp))); + Assert.That(_formatter.Format(cultureDE, fmt, temp), Is.EqualTo(string.Format(cultureDE, fmt, temp))); } [Test] @@ -82,18 +96,7 @@ public void NamedPlaceholderDateTime() var smartFmt = "It is now {Date:d} at {Date:t}"; var stringFmt = $"It is now {now.Date:d} at {now.Date:t}"; - Assert.AreEqual(stringFmt, Smart.Format(smartFmt, now)); - } - - [Test] - public void NamedPlaceholderDateTimeHHmmss() - { - // columns in the time part must not be recogniced as delimiters of a named placeholder - // (except a formatter's name would really be 'yyyy/MM/dd HH') - var now = DateTime.Now; - var smartFmt = "It is now {Date:yyyy/MM/dd HH:mm:ss}"; - var stringFmt = $"It is now {now.Date:yyyy/MM/dd HH:mm:ss}"; - Assert.AreEqual(stringFmt, Smart.Format(smartFmt, now)); + Assert.That(_formatter.Format(smartFmt, now), Is.EqualTo(stringFmt)); } [Test] @@ -103,7 +106,37 @@ public void NamedPlaceholderAlignment() var smartFmt = "Year: {Item1,-6} Amount: {Item2,15:N0}"; var stringFmt = $"Year: {yearAmount.Item1,-6} Amount: {yearAmount.Item2,15:N0}"; - Assert.AreEqual(stringFmt, Smart.Format(smartFmt, yearAmount)); + Assert.That(_formatter.Format(smartFmt, yearAmount), Is.EqualTo(stringFmt)); + } + + [Test] + public void NamedPlaceholder_DecimalArg() + { + _formatter.Settings.UseStringFormatCompatibility = false; + var pricePerOunce = 17.36m; + var format = "The current price is {0} per ounce."; + + Assert.That(_formatter.Format(format, pricePerOunce), Is.EqualTo(string.Format(format, pricePerOunce))); + } + + [Test] + public void NamedPlaceholder_DecimalCurrencyArg() + { + _formatter.Settings.UseStringFormatCompatibility = false; + var pricePerOunce = 17.36m; + var format = "The current price is {0:C2} per ounce."; + + Assert.That(_formatter.Format(format, pricePerOunce), Is.EqualTo(string.Format(format, pricePerOunce))); + } + + /// + /// Custom formatters are not recognized with string.Format compatibility switched ON + /// + [TestCase("{0:FormatterName(true|false):one|two|default}", true)] + [TestCase("{0:FormatterName(string|String):one|two|default}", "String")] + public void Choose_should_be_case_sensitive(string format, object arg0) + { + Assert.That(_formatter.Format(format, arg0), Does.StartWith("FormatterName")); } } } \ No newline at end of file diff --git a/src/SmartFormat.Tests/Extensions/ChooseFormatterTests.cs b/src/SmartFormat.Tests/Extensions/ChooseFormatterTests.cs index 481e21e2..34cafaed 100644 --- a/src/SmartFormat.Tests/Extensions/ChooseFormatterTests.cs +++ b/src/SmartFormat.Tests/Extensions/ChooseFormatterTests.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions.Formatting; using NUnit.Framework; using SmartFormat.Core.Formatting; using SmartFormat.Core.Settings; @@ -8,6 +9,7 @@ namespace SmartFormat.Tests.Extensions [TestFixture] public class ChooseFormatterTests { + private SmartFormatter _formatter = Smart.CreateDefaultSmartFormat(); [TestCase("{0:choose(1|2|3):one|two|three}", 1, "one")] [TestCase("{0:choose(1|2|3):one|two|three}", 2, "two")] @@ -29,7 +31,7 @@ public class ChooseFormatterTests [TestCase("{0:choose(True|False):yep|nope}", false, "nope")] public void Choose_should_work_with_numbers_strings_and_booleans(string format, object arg0, string expectedResult) { - Assert.AreEqual(expectedResult, Smart.Format(format, arg0)); + Assert.AreEqual(expectedResult, _formatter.Format(format, arg0)); } [TestCase("{0:choose(true|True):one|two|default}", true, "two")] @@ -40,7 +42,7 @@ public void Choose_should_work_with_numbers_strings_and_booleans(string format, [TestCase("{0:choose(ignore|IGNORE):one|two|default}", SmartFormat.Core.Settings.FormatErrorAction.Ignore, "default")] public void Choose_should_be_case_sensitive(string format, object arg0, string expectedResult) { - Assert.AreEqual(expectedResult, Smart.Format(format, arg0)); + Assert.AreEqual(expectedResult, _formatter.Format(format, arg0)); } [TestCase("{0:choose(1|2|3):one|two|three|default}", 1, "one")] @@ -53,7 +55,7 @@ public void Choose_should_be_case_sensitive(string format, object arg0, string e [TestCase("{0:choose(1|2|3):one|two|three|default}", "whatever", "default")] public void Choose_should_default_to_the_last_item(string format, object arg0, string expectedResult) { - Assert.AreEqual(expectedResult, Smart.Format(format, arg0)); + Assert.AreEqual(expectedResult, _formatter.Format(format, arg0)); } [TestCase("{0:choose(Male|Female):man|woman}", Gender.Male, "man")] @@ -62,7 +64,7 @@ public void Choose_should_default_to_the_last_item(string format, object arg0, s [TestCase("{0:choose(Male):man|woman}", Gender.Female, "woman")] public void Choose_should_work_with_enums(string format, object arg0, string expectedResult) { - Assert.AreEqual(expectedResult, Smart.Format(format, arg0)); + Assert.AreEqual(expectedResult, _formatter.Format(format, arg0)); } [TestCase("{0:choose(null):nothing|{} }", null, "nothing")] @@ -72,7 +74,7 @@ public void Choose_should_work_with_enums(string format, object arg0, string exp [TestCase("{0:choose(null|5):nothing|five|{} }", 6, "6 ")] public void Choose_has_a_special_case_for_null(string format, object arg0, string expectedResult) { - Assert.AreEqual(expectedResult, Smart.Format(format, arg0)); + Assert.AreEqual(expectedResult, _formatter.Format(format, arg0)); } [TestCase("{0:choose(1|2):1|2}", 99)] @@ -80,7 +82,7 @@ public void Choose_has_a_special_case_for_null(string format, object arg0, strin public void Choose_throws_when_choice_is_invalid(string format, object arg0) { Smart.Default.Settings.Formatter.ErrorAction = FormatErrorAction.ThrowError; - Assert.Throws(() => Smart.Format(format, arg0)); + Assert.Throws(() => _formatter.Format(format, arg0)); } // Too few choices: @@ -92,7 +94,7 @@ public void Choose_throws_when_choice_is_invalid(string format, object arg0) public void Choose_throws_when_choices_are_too_few_or_too_many(string format, object arg0) { Smart.Default.Settings.Formatter.ErrorAction = FormatErrorAction.ThrowError; - Assert.Throws(() => Smart.Format(format, arg0)); + Assert.Throws(() => _formatter.Format(format, arg0)); } } diff --git a/src/SmartFormat.Tests/Extensions/JsonSourceTests.cs b/src/SmartFormat.Tests/Extensions/JsonSourceTests.cs index 8cdbc96b..be0de31b 100644 --- a/src/SmartFormat.Tests/Extensions/JsonSourceTests.cs +++ b/src/SmartFormat.Tests/Extensions/JsonSourceTests.cs @@ -12,9 +12,11 @@ namespace SmartFormat.Tests.Extensions [TestFixture] public class JsonSourceTests { + private SmartFormatter _formatter = Smart.CreateDefaultSmartFormat(); + public JsonSourceTests() { - Smart.Default.SourceExtensions.Add(new JsonSource(Smart.Default)); + _formatter.SourceExtensions.Add(new JsonSource(Smart.Default)); } private const string JsonOneLevel = @"{'Name': 'Doe'}"; @@ -60,7 +62,7 @@ public JsonSourceTests() public void NS_Format_Null_Json() { var jObject = JObject.Parse(JsonNull); - var result = Smart.Format("{Name}", jObject); + var result = _formatter.Format("{Name}", jObject); Assert.AreEqual("", result); } @@ -68,7 +70,7 @@ public void NS_Format_Null_Json() public void NS_Format_OneLevel_Json() { var jObject = JObject.Parse(JsonOneLevel); - var result = Smart.Format("{Name}", jObject); + var result = _formatter.Format("{Name}", jObject); Assert.AreEqual("Doe", result); } @@ -76,7 +78,7 @@ public void NS_Format_OneLevel_Json() public void NS_Format_TwoLevel_Json() { var jObject = JObject.Parse(JsonTwoLevel); - var result = Smart.Format("{Name.First}", jObject); + var result = _formatter.Format("{Name.First}", jObject); Assert.AreEqual("Joe", result); } @@ -88,9 +90,9 @@ public void NS_Format_Complex_Json() Smart.Default.Settings.CaseSensitivity = CaseSensitivityType.CaseSensitive; Assert.Multiple(() => { - Assert.AreEqual("50.00", Smart.Format(CultureInfo.InvariantCulture, "{Manufacturers[0].Products[0].Price:0.00}", jObject)); - Assert.AreEqual("True", Smart.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[0].OnStock}", jObject)); - Assert.AreEqual("False", Smart.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[1].OnStock}", jObject)); + Assert.AreEqual("50.00", _formatter.Format(CultureInfo.InvariantCulture, "{Manufacturers[0].Products[0].Price:0.00}", jObject)); + Assert.AreEqual("True", _formatter.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[0].OnStock}", jObject)); + Assert.AreEqual("False", _formatter.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[1].OnStock}", jObject)); }); Smart.Default.Settings.CaseSensitivity = savedSetting; @@ -100,17 +102,18 @@ public void NS_Format_Complex_Json() public void NS_Format_Complex_Json_CaseInsensitive() { var jObject = JObject.Parse(JsonComplex); - var savedSetting = Smart.Default.Settings.CaseSensitivity = CaseSensitivityType.CaseInsensitive; - var result = Smart.Format(CultureInfo.InvariantCulture, "{MaNuFaCtUrErS[0].PrOdUcTs[0].PrIcE:0.00}", jObject); + var savedSetting = _formatter.Settings.CaseSensitivity; + _formatter.Settings.CaseSensitivity = CaseSensitivityType.CaseInsensitive; + var result = _formatter.Format(CultureInfo.InvariantCulture, "{MaNuFaCtUrErS[0].PrOdUcTs[0].PrIcE:0.00}", jObject); Assert.AreEqual("50.00", result); - Smart.Default.Settings.CaseSensitivity = savedSetting; + _formatter.Settings.CaseSensitivity = savedSetting; } [Test] public void NS_Format_List_Json() { var jObject = JObject.Parse(JsonComplex); - var result = Smart.Format("{Stores:list:{}|, |, and }", jObject); + var result = _formatter.Format("{Stores:list:{}|, |, and }", jObject); Assert.AreEqual("Lambton Quay, and Willis Street", result); } @@ -120,7 +123,7 @@ public void NS_Format_Exception_Json() var savedSetting = Smart.Default.Settings.FormatErrorAction; Smart.Default.Settings.Formatter.ErrorAction = FormatErrorAction.ThrowError; var jObject = JObject.Parse(JsonOneLevel); - Assert.Throws(() => Smart.Format("{Dummy}", jObject)); + Assert.Throws(() => _formatter.Format("{Dummy}", jObject)); Smart.Default.Settings.FormatErrorAction = savedSetting; } @@ -132,7 +135,7 @@ public void NS_Format_Exception_Json() public void ST_Format_Null_Json() { var jObject = JsonDocument.Parse(JsonNull.Replace("'", "\"")).RootElement; - var result = Smart.Format("{Name}", jObject); + var result = _formatter.Format("{Name}", jObject); Assert.AreEqual("", result); } @@ -140,7 +143,7 @@ public void ST_Format_Null_Json() public void ST_Format_OneLevel_Json() { var jObject = JsonDocument.Parse(JsonOneLevel.Replace("'", "\"")).RootElement; - var result = Smart.Format("{Name}", jObject); + var result = _formatter.Format("{Name}", jObject); Assert.AreEqual("Doe", result); } @@ -148,7 +151,7 @@ public void ST_Format_OneLevel_Json() public void ST_Format_TwoLevel_Json() { var jObject = JsonDocument.Parse(JsonTwoLevel.Replace("'", "\"")).RootElement; - var result = Smart.Format("{Name.First}", jObject); + var result = _formatter.Format("{Name.First}", jObject); Assert.AreEqual("Joe", result); } @@ -160,9 +163,9 @@ public void ST_Format_Complex_Json() Smart.Default.Settings.CaseSensitivity = CaseSensitivityType.CaseSensitive; Assert.Multiple(() => { - Assert.AreEqual("50.00", Smart.Format(CultureInfo.InvariantCulture, "{Manufacturers[0].Products[0].Price:0.00}", jObject)); - Assert.AreEqual("True", Smart.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[0].OnStock}", jObject)); - Assert.AreEqual("False", Smart.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[1].OnStock}", jObject)); + Assert.AreEqual("50.00", _formatter.Format(CultureInfo.InvariantCulture, "{Manufacturers[0].Products[0].Price:0.00}", jObject)); + Assert.AreEqual("True", _formatter.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[0].OnStock}", jObject)); + Assert.AreEqual("False", _formatter.Format(CultureInfo.InvariantCulture, "{Manufacturers[1].Products[1].OnStock}", jObject)); }); Smart.Default.Settings.CaseSensitivity = savedSetting; } @@ -172,8 +175,8 @@ public void ST_Format_Complex_Json_CaseInsensitive() { var jObject = JsonDocument.Parse(JsonComplex.Replace("'", "\"")).RootElement; var savedSetting = Smart.Default.Settings.CaseSensitivity; - Smart.Default.Settings.CaseSensitivity = CaseSensitivityType.CaseInsensitive; - var result = Smart.Format(CultureInfo.InvariantCulture, "{MaNuFaCtUrErS[0].PrOdUcTs[0].PrIcE:0.00}", jObject); + _formatter.Settings.CaseSensitivity = CaseSensitivityType.CaseInsensitive; + var result = _formatter.Format(CultureInfo.InvariantCulture, "{MaNuFaCtUrErS[0].PrOdUcTs[0].PrIcE:0.00}", jObject); Assert.AreEqual("50.00", result); Smart.Default.Settings.CaseSensitivity = savedSetting; } @@ -182,7 +185,7 @@ public void ST_Format_Complex_Json_CaseInsensitive() public void ST_Format_List_Json() { var jObject = JsonDocument.Parse(JsonComplex.Replace("'", "\"")).RootElement; - var result = Smart.Format("{Stores:list:{}|, |, and }", jObject); + var result = _formatter.Format("{Stores:list:{}|, |, and }", jObject); Assert.AreEqual("Lambton Quay, and Willis Street", result); } @@ -192,7 +195,7 @@ public void ST_Format_Exception_Json() var savedSetting = Smart.Default.Settings.FormatErrorAction; Smart.Default.Settings.Formatter.ErrorAction = FormatErrorAction.ThrowError; var jObject = JsonDocument.Parse(JsonOneLevel.Replace("'", "\"")).RootElement; - Assert.Throws(() => Smart.Format("{Dummy}", jObject)); + Assert.Throws(() => _formatter.Format("{Dummy}", jObject)); Smart.Default.Settings.FormatErrorAction = savedSetting; } diff --git a/src/SmartFormat.Tests/Extensions/XmlSourceTest.cs b/src/SmartFormat.Tests/Extensions/XmlSourceTest.cs index 48213e4f..25f02284 100644 --- a/src/SmartFormat.Tests/Extensions/XmlSourceTest.cs +++ b/src/SmartFormat.Tests/Extensions/XmlSourceTest.cs @@ -75,7 +75,7 @@ public void Format_XmlWithNamespaces_IgnoringNamespace() public void Format_SingleLevelXml_TemplateWithCurlyBraces_Escaped() { var sf = Smart.CreateDefaultSmartFormat(); - sf.Settings.Parser.UseStringFormatCompatibility = true; + sf.Settings.UseStringFormatCompatibility = true; // arrange var xmlEl = XElement.Parse(OneLevelXml); // act diff --git a/src/SmartFormat/Core/Parsing/EscapedLiteral.cs b/src/SmartFormat/Core/Parsing/EscapedLiteral.cs index 7e26b4b4..af5624b5 100644 --- a/src/SmartFormat/Core/Parsing/EscapedLiteral.cs +++ b/src/SmartFormat/Core/Parsing/EscapedLiteral.cs @@ -27,7 +27,8 @@ public static class EscapedLiteral {'n', '\n'}, {'r', '\r'}, {'t', '\t'}, - {'v', '\v'} + {'v', '\v'}, + {':', ':'} // escaped colons can be used anywhere in the format string }; private static readonly Dictionary FormatterOptionsLookupTable = new() { @@ -35,8 +36,7 @@ public static class EscapedLiteral {'(', '('}, {')', ')'}, {'{', '{'}, - {'}', '}'}, - {':', ':'} + {'}', '}'} }; /// @@ -79,7 +79,6 @@ private static char GetUnicode(string input, int startIndex) /// The input with escaped characters with their real value. public static ReadOnlySpan UnEscapeCharLiterals(char escapingSequenceStart, string input, int startIndex, int length, bool includeFormatterOptionChars) { - //var length = input.Length; // It's enough to have a buffer with the same size as input length var result = new Span(new char[length]); var max = startIndex + length; diff --git a/src/SmartFormat/Core/Parsing/Parser.cs b/src/SmartFormat/Core/Parsing/Parser.cs index e9cd2eab..7ff361d6 100644 --- a/src/SmartFormat/Core/Parsing/Parser.cs +++ b/src/SmartFormat/Core/Parsing/Parser.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using SmartFormat.Core.Settings; @@ -42,6 +43,10 @@ public class Parser #region: Constructor : + /// + /// Creates a new instance of a . + /// + /// internal Parser(SmartSettings smartSettings) { Settings = smartSettings; @@ -92,7 +97,7 @@ public void AddOperators(string chars) [Obsolete("Use 'Settings.Parser.UseStringFormatCompatibility' instead.")] public void UseAlternativeEscapeChar(char alternativeEscapeChar = '\\') { - _parserSettings.UseStringFormatCompatibility = false; + Settings.UseStringFormatCompatibility = false; _parserSettings.CharLiteralEscapeChar = alternativeEscapeChar; } @@ -105,7 +110,7 @@ public void UseAlternativeEscapeChar(char alternativeEscapeChar = '\\') [Obsolete("Use 'Settings.Parser.UseStringFormatCompatibility' instead.")] public void UseBraceEscaping() { - _parserSettings.UseStringFormatCompatibility = true; + Settings.UseStringFormatCompatibility = true; } /// @@ -190,9 +195,8 @@ public int SafeAdd(int index, int add) /// Parses a format string. /// /// - /// /// The for the parsed string. - public Format ParseFormat(string inputFormat, string[] formatterExtensionNames) + public Format ParseFormat(string inputFormat) { var index = new IndexContainer { ObjectLength = inputFormat.Length, @@ -241,13 +245,13 @@ public Format ParseFormat(string inputFormat, string[] formatterExtensionNames) FinishPlaceholderFormat(ref currentFormat, ref nestedDepth, ref index); } else if (inputChar == _parserSettings.CharLiteralEscapeChar && _parserSettings.ConvertCharacterStringLiterals || - !_parserSettings.UseStringFormatCompatibility && inputChar == _parserSettings.CharLiteralEscapeChar) + !Settings.UseStringFormatCompatibility && inputChar == _parserSettings.CharLiteralEscapeChar) { ParseAlternativeEscaping(inputFormat, currentFormat, ref index); } else if (index.NamedFormatterStart != PositionUndefined) { - if(!ParseNamedFormatter(inputFormat, currentFormat, ref index, formatterExtensionNames)) continue; + if(!ParseNamedFormatter(inputFormat, currentFormat, ref index)) continue; } } else @@ -273,12 +277,14 @@ public Format ParseFormat(string inputFormat, string[] formatterExtensionNames) currentFormat.Items.Add(new LiteralText(Settings, currentFormat, index.LastEnd, inputFormat.Length)); } - // Todo v2.7.0: There is no unit test for this condition! + Debug.Assert(currentFormat.Parent == null); + /* Not hit by any unit test in v2.7.0 and v3.0.0 while (currentFormat.Parent != null) { currentFormat = currentFormat.Parent.Parent; currentFormat.EndIndex = inputFormat.Length; } + */ // Check for any parsing errors: if (parsingErrors.HasIssues) @@ -342,7 +348,7 @@ private bool HasProcessedTooMayClosingBraces(Format currentFormat, ParsingErrors [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool EscapeLikeStringFormat(string inputFormat, ref IndexContainer index, char brace) { - if (!_parserSettings.UseStringFormatCompatibility) return false; + if (!Settings.UseStringFormatCompatibility) return false; if (index.LastEnd < inputFormat.Length && inputFormat[index.LastEnd] == brace) { @@ -449,9 +455,8 @@ private void ParseAlternativeEscaping(string inputFormat, Format currentFormat, /// The input format string. /// The current . /// The . - /// The registered formatter names [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool ParseNamedFormatter(string inputFormat, Format currentFormat, ref IndexContainer index, string[] formatterExtensionNames) + private bool ParseNamedFormatter(string inputFormat, Format currentFormat, ref IndexContainer index) { var inputChar = inputFormat[index.Current]; if (inputChar == _parserSettings.FormatterOptionsBeginChar) @@ -502,36 +507,32 @@ private bool ParseNamedFormatter(string inputFormat, Format currentFormat, ref I var parentPlaceholder = currentFormat.Parent; - if (index.NamedFormatterOptionsStart == PositionUndefined) + if (Settings.UseStringFormatCompatibility) { - var formatterName = inputFormat.Substring(index.NamedFormatterStart, - index.Current - index.NamedFormatterStart); - - if (FormatterNameExists(formatterName, formatterExtensionNames)) - { - if (parentPlaceholder != null) parentPlaceholder.FormatterName = formatterName; - } - else - index.LastEnd = currentFormat.StartIndex; + // named formatters will not be parsed with string.Format compatibility switched ON, + // but we can handle e.g. Smart.Format("{Date:yyyy/MM/dd HH:mm:ss}") like string.Format + index.LastEnd = currentFormat.StartIndex; } else { - var formatterName = inputFormat.Substring(index.NamedFormatterStart, - index.NamedFormatterOptionsStart - index.NamedFormatterStart); - - if (FormatterNameExists(formatterName, formatterExtensionNames)) + if (index.NamedFormatterOptionsStart == PositionUndefined) { if (parentPlaceholder != null) - { - parentPlaceholder.FormatterName = formatterName; - // Save the formatter options with CharLiteralEscapeChar removed - parentPlaceholder.FormatterOptionsRaw = inputFormat.Substring(index.NamedFormatterOptionsStart + 1, - index.NamedFormatterOptionsEnd - (index.NamedFormatterOptionsStart + 1)); - } + parentPlaceholder.FormatterName = inputFormat.Substring(index.NamedFormatterStart, + index.Current - index.NamedFormatterStart); } else { - index.LastEnd = currentFormat.StartIndex; + if (parentPlaceholder != null) + { + parentPlaceholder.FormatterName = inputFormat.Substring(index.NamedFormatterStart, + index.NamedFormatterOptionsStart - index.NamedFormatterStart); + + // Save the formatter options with CharLiteralEscapeChar removed + parentPlaceholder.FormatterOptionsRaw = inputFormat.Substring( + index.NamedFormatterOptionsStart + 1, + index.NamedFormatterOptionsEnd - (index.NamedFormatterOptionsStart + 1)); + } } } diff --git a/src/SmartFormat/Core/Settings/ParserSettings.cs b/src/SmartFormat/Core/Settings/ParserSettings.cs index d7b2e90e..9b83a5b4 100644 --- a/src/SmartFormat/Core/Settings/ParserSettings.cs +++ b/src/SmartFormat/Core/Settings/ParserSettings.cs @@ -94,12 +94,6 @@ public void AddCustomOperatorChars(IList characters) } } - /// - /// Uses string.Format-compatible escaping of curly braces, {{ and }}, - /// instead of the Smart.Format default escaping, \{ and \}, - /// - public bool UseStringFormatCompatibility { get; set; } = false; - /// /// This setting is relevant for the . /// If (the default), character string literals are treated like in "normal" string.Format: diff --git a/src/SmartFormat/Core/Settings/SmartSettings.cs b/src/SmartFormat/Core/Settings/SmartSettings.cs index 0ccd377f..afa8e62e 100644 --- a/src/SmartFormat/Core/Settings/SmartSettings.cs +++ b/src/SmartFormat/Core/Settings/SmartSettings.cs @@ -20,6 +20,13 @@ internal SmartSettings() { } + /// + /// Uses string.Format-compatible escaping of curly braces, {{ and }}, + /// instead of the Smart.Format default escaping, \{ and \}. + /// Custom formatters cannot be parsed. + /// + public bool UseStringFormatCompatibility { get; set; } = false; + /// /// Gets or sets the to apply for the . /// The default is . diff --git a/src/SmartFormat/Extensions/TemplateFormatter.cs b/src/SmartFormat/Extensions/TemplateFormatter.cs index 2f385a7e..cb682c57 100644 --- a/src/SmartFormat/Extensions/TemplateFormatter.cs +++ b/src/SmartFormat/Extensions/TemplateFormatter.cs @@ -70,7 +70,7 @@ public bool TryEvaluateFormat(IFormattingInfo formattingInfo) /// The string to be used as a template. public void Register(string templateName, string template) { - var parsed = _formatter.Parser.ParseFormat(template, _formatter.GetNotEmptyFormatterExtensionNames()); + var parsed = _formatter.Parser.ParseFormat(template); _templates.Add(templateName, parsed); } diff --git a/src/SmartFormat/SmartFormatter.cs b/src/SmartFormat/SmartFormatter.cs index cebee5b1..8c897eb1 100644 --- a/src/SmartFormat/SmartFormatter.cs +++ b/src/SmartFormat/SmartFormatter.cs @@ -172,7 +172,7 @@ public string Format(IFormatProvider? provider, string format, params object[] a public string Format(IFormatProvider? provider, string format, IList args) { var output = new StringOutput(format.Length + args.Count * 8); - var formatParsed = Parser.ParseFormat(format, GetNotEmptyFormatterExtensionNames()); + var formatParsed = Parser.ParseFormat(format); var current = args.Count > 0 ? args[0] : args; // The first item is the default. var formatDetails = new FormatDetails(this, formatParsed, args, null, provider, output); Format(formatDetails, formatParsed, current); @@ -199,7 +199,7 @@ public void FormatInto(IOutput output, string format, params object[] args) /// The objects to format. public void FormatInto(IOutput output, string format, IList args) { - var formatParsed = Parser.ParseFormat(format, GetNotEmptyFormatterExtensionNames()); + var formatParsed = Parser.ParseFormat(format); var current = args.Count > 0 ? args[0] : args; // The first item is the default. var formatDetails = new FormatDetails(this, formatParsed, args, null, null, output); Format(formatDetails, formatParsed, current); @@ -230,7 +230,7 @@ public string FormatWithCache(ref FormatCache? cache, string format, IList 0 ? args[0] : args; // The first item is the default. var formatDetails = new FormatDetails(this, cache.Format, args, cache, null, output); Format(formatDetails, cache.Format, current); @@ -259,7 +259,7 @@ public void FormatWithCacheInto(ref FormatCache cache, IOutput output, string fo /// The objects to format. public void FormatWithCacheInto(ref FormatCache cache, IOutput output, string format, IList args) { - cache ??= new FormatCache(Parser.ParseFormat(format, GetNotEmptyFormatterExtensionNames())); + cache ??= new FormatCache(Parser.ParseFormat(format)); var current = args.Count > 0 ? args[0] : args; // The first item is the default. var formatDetails = new FormatDetails(this, cache.Format, args, cache, null, output); Format(formatDetails, cache.Format, current);