From 6b214741dd65344b4a2cad658f220f7477989fca Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 19:37:54 +0000 Subject: [PATCH] Write a quoted empty default as "" instead of dropping it [patch] With DefaultValueIsQuoted set, empty raw text is the empty string, not "no initialiser". WriteDefaultValueTo and PropertyTemplate now treat a default as set when it is quoted or non-empty, so `string separator = ""` stays optional and `const string Empty = ""` keeps its value. Fixes #174 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017vfJW4VgeC1VNAtTYhxCrm --- CodeBlocker.Test/TemplateTests.cs | 78 +++++++++++++++++++++++ CodeBlocker/Templates/PropertyTemplate.cs | 6 +- CodeBlocker/Templates/TemplateBase.cs | 13 +++- 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/CodeBlocker.Test/TemplateTests.cs b/CodeBlocker.Test/TemplateTests.cs index cbe4b4a..a621fe1 100644 --- a/CodeBlocker.Test/TemplateTests.cs +++ b/CodeBlocker.Test/TemplateTests.cs @@ -74,6 +74,35 @@ public void ParameterEscapesALoneSurrogateInAQuotedDefaultValue() } } + [TestMethod] + public void ParameterWritesAQuotedEmptyDefaultValue() => + Assert.AreEqual( + "string separator = \"\"", + Render(new ParameterTemplate { Type = "string", Name = "separator", DefaultValue = "", DefaultValueIsQuoted = true })); + + [TestMethod] + public void ParameterWithAnEmptyUnquotedDefaultValueHasNone() => + Assert.AreEqual("string separator", Render(new ParameterTemplate { Type = "string", Name = "separator", DefaultValue = "" })); + + [TestMethod] + public void AQuotedEmptyDefaultKeepsAParameterOptional() + { + MethodTemplate method = new() + { + Type = "void", + Name = "M", + Keywords = { "public" }, + BodyFactory = _ => { }, + Parameters = + { + new ParameterTemplate { Type = "int", Name = "count", DefaultValue = "0" }, + new ParameterTemplate { Type = "string", Name = "separator", DefaultValue = "", DefaultValueIsQuoted = true }, + }, + }; + + Assert.AreEqual("public void M(int count = 0, string separator = \"\") { }\n", Render(method)); + } + [TestMethod] public void ParameterKeepsItsAttributesOnTheDeclarationLine() { @@ -102,6 +131,12 @@ public void FieldEscapesAQuotedInitialiser() => "private string p = \"C:\\\\temp\\\\\\\"x\\\"\";\n", Render(new FieldTemplate { Type = "string", Name = "p", Keywords = { "private" }, DefaultValue = "C:\\temp\\\"x\"", DefaultValueIsQuoted = true })); + [TestMethod] + public void FieldWritesAQuotedEmptyInitialiser() => + Assert.AreEqual( + "public const string Empty = \"\";\n", + Render(new FieldTemplate { Type = "string", Name = "Empty", Keywords = { "public", "const" }, DefaultValue = "", DefaultValueIsQuoted = true })); + [TestMethod] public void FieldCommentsAndAttributesGoOnTheirOwnLines() { @@ -190,6 +225,49 @@ public void AShorthandPropertyKeepsItsInitialiser() Assert.AreEqual("public int Count { get; set; } = 7;\n", Render(property)); } + [TestMethod] + public void AShorthandPropertyKeepsAQuotedEmptyInitialiser() + { + PropertyTemplate property = new() + { + Type = "string", + Name = "Prefix", + Keywords = { "public" }, + Getter = AccessorTemplate.Auto(), + Setter = AccessorTemplate.Auto(), + DefaultValue = "", + DefaultValueIsQuoted = true, + }; + + Assert.AreEqual("public string Prefix { get; set; } = \"\";\n", Render(property)); + } + + [TestMethod] + public void ABracedPropertyKeepsAQuotedEmptyInitialiser() + { + PropertyTemplate property = new() + { + Type = "string", + Name = "Prefix", + Keywords = { "public" }, + Getter = AccessorTemplate.Auto(), + Setter = new AccessorTemplate { Modifier = "private" }, + DefaultValue = "", + DefaultValueIsQuoted = true, + }; + + Assert.AreEqual( + """ + public string Prefix + { + get; + private set; + } = ""; + + """.ReplaceLineEndings("\n"), + Render(property)); + } + [TestMethod] public void AnAccessorModifierForcesTheBracedForm() { diff --git a/CodeBlocker/Templates/PropertyTemplate.cs b/CodeBlocker/Templates/PropertyTemplate.cs index d6bc947..df7977a 100644 --- a/CodeBlocker/Templates/PropertyTemplate.cs +++ b/CodeBlocker/Templates/PropertyTemplate.cs @@ -120,11 +120,9 @@ private void WriteShorthand(CodeBlocker codeBlocker) codeBlocker.Write(" }"); WriteDefaultValueTo(codeBlocker); - codeBlocker.WriteLine(DefaultValueIsSet ? ";" : string.Empty); + codeBlocker.WriteLine(HasDefaultValue ? ";" : string.Empty); } - private bool DefaultValueIsSet => !string.IsNullOrEmpty(DefaultValue); - private void WriteAccessorList(CodeBlocker codeBlocker) { codeBlocker.WriteLine(); @@ -140,6 +138,6 @@ private void WriteAccessorList(CodeBlocker codeBlocker) // Scope would not allow. codeBlocker.Write("}"); WriteDefaultValueTo(codeBlocker); - codeBlocker.WriteLine(DefaultValueIsSet ? ";" : string.Empty); + codeBlocker.WriteLine(HasDefaultValue ? ";" : string.Empty); } } diff --git a/CodeBlocker/Templates/TemplateBase.cs b/CodeBlocker/Templates/TemplateBase.cs index ee31222..baffd64 100644 --- a/CodeBlocker/Templates/TemplateBase.cs +++ b/CodeBlocker/Templates/TemplateBase.cs @@ -22,7 +22,8 @@ public abstract class TemplateBase public string Type { get; set; } = string.Empty; /// - /// Gets or sets the initialiser or default value. Empty means no initialiser is written. + /// Gets or sets the initialiser or default value. Empty means no initialiser is written, unless + /// is set, in which case it is the empty string "". /// public string DefaultValue { get; set; } = string.Empty; @@ -33,6 +34,12 @@ public abstract class TemplateBase /// public bool DefaultValueIsQuoted { get; set; } + /// + /// Gets a value indicating whether an initialiser is written: is not + /// empty, or it is quoted, where empty raw text is the empty string literal. + /// + private protected bool HasDefaultValue => DefaultValueIsQuoted || !string.IsNullOrEmpty(DefaultValue); + /// /// Gets the attributes to write before the declaration, each without its square brackets. /// @@ -79,7 +86,7 @@ public virtual void WriteTo(CodeBlocker codeBlocker) } /// - /// Writes = value when is set, as an escaped string literal + /// Writes = value when is set, as an escaped string literal /// when is set. /// /// The to write to. @@ -87,7 +94,7 @@ protected void WriteDefaultValueTo(CodeBlocker codeBlocker) { Ensure.NotNull(codeBlocker); - if (string.IsNullOrEmpty(DefaultValue)) + if (!HasDefaultValue) { return; }