diff --git a/CodeBlocker.Test/TemplateTests.cs b/CodeBlocker.Test/TemplateTests.cs
index 999de40..dae094a 100644
--- a/CodeBlocker.Test/TemplateTests.cs
+++ b/CodeBlocker.Test/TemplateTests.cs
@@ -1094,6 +1094,37 @@ public void ASourceFileWritesItsPreambleThenItsTypes()
Render(codeBlocker => codeBlocker.AddSourceFile(file)));
}
+ [TestMethod]
+ public void ASourceFileWritesItsGlobalAttributesAfterItsUsingsAndBeforeItsNamespace()
+ {
+ SourceFileTemplate file = new()
+ {
+ Namespace = "Contoso",
+ Usings = { "System" },
+ Attributes = { "assembly: CLSCompliant(true)" },
+ Classes = { new ClassTemplate { Name = "Widget", Keywords = { "public" } } },
+ };
+
+ Assert.AreEqual(
+ "using System;\n\n[assembly: CLSCompliant(true)]\n\nnamespace Contoso;\n\npublic class Widget\n{\n}\n\n",
+ Render(codeBlocker => codeBlocker.AddSourceFile(file)));
+ }
+
+ [TestMethod]
+ public void ASourceFileWithNoNamespaceWritesItsGlobalAttributesAfterItsUsings()
+ {
+ SourceFileTemplate file = new()
+ {
+ Usings = { "System" },
+ Attributes = { "assembly: CLSCompliant(true)" },
+ Classes = { new ClassTemplate { Name = "Widget", Keywords = { "public" } } },
+ };
+
+ Assert.AreEqual(
+ "using System;\n\n[assembly: CLSCompliant(true)]\n\npublic class Widget\n{\n}\n\n",
+ Render(codeBlocker => codeBlocker.AddSourceFile(file)));
+ }
+
#endregion
#region Null handling
diff --git a/CodeBlocker/Templates/SourceFileTemplate.cs b/CodeBlocker/Templates/SourceFileTemplate.cs
index cb15d10..9b644f9 100644
--- a/CodeBlocker/Templates/SourceFileTemplate.cs
+++ b/CodeBlocker/Templates/SourceFileTemplate.cs
@@ -39,6 +39,13 @@ public static class SourceFileTemplateExtensions
/// The to write to.
/// The file to write.
/// The same , for chaining.
+ ///
+ /// The only attributes a file can carry are global ones (assembly:, module:), and
+ /// C# only accepts them after every using directive and before the namespace. So when the file
+ /// has attributes, its usings come first, then the attributes, then the namespace; that is the one
+ /// order in which an attribute can be named through the file's own usings. A file with no
+ /// attributes keeps the usings inside its file-scoped namespace.
+ ///
///
/// or is .
///
@@ -47,9 +54,23 @@ public static CodeBlocker AddSourceFile(this CodeBlocker codeBlocker, SourceFile
Ensure.NotNull(codeBlocker);
Ensure.NotNull(template);
- codeBlocker.AddTemplate(template);
- codeBlocker.WriteFileScopedNamespace(template.Namespace);
- codeBlocker.WriteUsings(template.Usings);
+ codeBlocker.AddDocumentation(template.Documentation);
+ codeBlocker.AddComments(template.Comments);
+ if (template.Attributes.Count > 0)
+ {
+ codeBlocker.WriteUsings(template.Usings);
+ codeBlocker.AddAttributes(template.Attributes);
+ codeBlocker.NewLine();
+ codeBlocker.AddKeywords(template.Keywords);
+ codeBlocker.WriteFileScopedNamespace(template.Namespace);
+ }
+ else
+ {
+ codeBlocker.AddKeywords(template.Keywords);
+ codeBlocker.WriteFileScopedNamespace(template.Namespace);
+ codeBlocker.WriteUsings(template.Usings);
+ }
+
codeBlocker.AddClasses(template.Classes);
return codeBlocker;
}