Skip to content

Instantly share code, notes, and snippets.

@martincostello
Last active May 21, 2022 10:54
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martincostello/2fa0a73c4ab7ae0d3ae46a8d435bc8a8 to your computer and use it in GitHub Desktop.
Save martincostello/2fa0a73c4ab7ae0d3ae46a8d435bc8a8 to your computer and use it in GitHub Desktop.
Converts C# source files to use file-scoped namespaces
// There may be corner cases where the naive string matching incorrectly
// changes your source file. Check your diff before committing any changes.
using System.Text;
// Change to whatever your favourite indentation is
const string Indent = " ";
// Get all the C# files in the specified directory
var fileNames = Directory.GetFiles(args[0], "*.cs", SearchOption.AllDirectories);
foreach (var fileName in fileNames)
{
// Ignore any files that are auto-generated
if (fileName.EndsWith(".designer.cs", StringComparison.OrdinalIgnoreCase) ||
fileName.EndsWith(".g.cs", StringComparison.OrdinalIgnoreCase) ||
fileName.EndsWith(".generated.cs", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var hasBom = HasBom(fileName);
var text = await File.ReadAllLinesAsync(fileName, Encoding.UTF8);
bool foundNamespace = false;
bool hasFileScopedNamespaceAlready = false;
bool isDirty = false;
for (int i = 0; i < text.Length; i++)
{
var line = text[i];
if ((!hasFileScopedNamespaceAlready && line.StartsWith('{')) ||
(line.StartsWith('}') && line.Trim().Length == 1 ))
{
// Remove the starting and trailing namespace braces
line = string.Empty;
isDirty = true;
}
else if (line.StartsWith("namespace "))
{
foundNamespace = true;
if (line.Contains(';'))
{
hasFileScopedNamespaceAlready = true;
break;
}
// Convert to a file-scoped namespace
line += ';';
isDirty = true;
}
else if (line.StartsWith(Indent))
{
// Remove one level of indentation
line = line[Indent.Length..];
isDirty = true;
}
text[i] = line;
}
if (foundNamespace && !hasFileScopedNamespaceAlready && isDirty)
{
// Remove the extra blank line at the end of the file left
// over from removing the closing brace from the namespace.
if (string.IsNullOrEmpty(text[^1]))
{
text = text.Take(text.Length - 1).ToArray();
}
await File.WriteAllLinesAsync(fileName, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: hasBom));
}
}
static bool HasBom(string fileName)
{
// From https://stackoverflow.com/a/19283954/1064169
var bom = new byte[4];
using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
file.Read(bom, 0, bom.Length);
}
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return true;
if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0) return true;
if (bom[0] == 0xff && bom[1] == 0xfe) return true;
if (bom[0] == 0xfe && bom[1] == 0xff) return true;
if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return true;
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment