Last active
May 21, 2022 10:54
-
-
Save martincostello/2fa0a73c4ab7ae0d3ae46a8d435bc8a8 to your computer and use it in GitHub Desktop.
Converts C# source files to use file-scoped namespaces
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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