diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..744a6af
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,33 @@
+version: 2
+updates:
+ - package-ecosystem: "nuget"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ labels:
+ - "dependencies"
+ commit-message:
+ prefix: "build"
+ include: "scope"
+ groups:
+ xunit:
+ patterns:
+ - "xunit*"
+ - package-ecosystem: "npm"
+ directory: "src/projektanker.code-butler"
+ schedule:
+ interval: "weekly"
+ labels:
+ - "dependencies"
+ commit-message:
+ prefix: "build"
+ include: "scope"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ labels:
+ - "dependencies"
+ commit-message:
+ prefix: "build"
+ include: "scope"
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index bd089e1..5fafa21 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -20,12 +20,13 @@ jobs:
uses: actions/checkout@v3
- name: 🟣 Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
+ uses: actions/setup-dotnet@v4
+
+ - name: 🛠️ Build
+ run: dotnet build
- name: ✔ Test
- run: dotnet test --verbosity normal
+ run: dotnet test --no-build --verbosity normal
- name: 📯 Publish
run: dotnet publish ./CodeButler.Console/CodeButler.Console.csproj --output ./publish
@@ -50,9 +51,7 @@ jobs:
uses: actions/setup-node@v3
- name: 🟣 Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
+ uses: actions/setup-dotnet@v4
- name: 🛠 Install NPM packages
run: npm ci
@@ -83,9 +82,7 @@ jobs:
uses: actions/setup-node@v3
- name: 🟣 Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
+ uses: actions/setup-dotnet@v4
- name: 🛠 Install NPM packages
run: npm ci
@@ -110,9 +107,7 @@ jobs:
uses: actions/setup-node@v3
- name: 🟣 Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
+ uses: actions/setup-dotnet@v4
- name: 🛠 Install NPM packages
run: npm ci
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..3b1d787
--- /dev/null
+++ b/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "version": "8.0.0",
+ "rollForward": "latestFeature"
+ }
+}
\ No newline at end of file
diff --git a/src/CodeButler/.editorconfig b/src/CodeButler/.editorconfig
index 94b46a3..47af26e 100644
--- a/src/CodeButler/.editorconfig
+++ b/src/CodeButler/.editorconfig
@@ -29,9 +29,18 @@ dotnet_diagnostic.IDE0072.severity = suggestion
# SA0001: XML comment analysis is disabled due to project configuration
dotnet_diagnostic.SA0001.severity = none
+# SA1000: The keyword 'new' should be followed by a space
+dotnet_diagnostic.SA1000.severity = none
+
+# SA1009: Closing parenthesis must be spaced correctly
+dotnet_diagnostic.SA1009.severity = silent
+
# SA1101: Prefix local calls with this
dotnet_diagnostic.SA1101.severity = none
+# SA1111: Closing parenthesis must be on line of last parameter
+dotnet_diagnostic.SA1111.severity = silent
+
# SA1123: Region should not be located within a code element
dotnet_diagnostic.SA1123.severity = suggestion
@@ -93,18 +102,22 @@ dotnet_naming_symbols.private_constant.required_modifiers = const
dotnet_naming_style.underscore_camel_case.required_prefix = _
dotnet_naming_style.underscore_camel_case.capitalization = camel_case
-#upper_case
+# upper_case
dotnet_naming_style.upper_case.capitalization = all_upper
dotnet_naming_style.upper_case.word_separator = _
#.NET naming rule:
-#private_fields_underscore_camelcase
+# private_fields_underscore_camelcase
dotnet_naming_rule.private_fields_underscore_camelcase.symbols = private_field
dotnet_naming_rule.private_fields_underscore_camelcase.style = underscore_camel_case
dotnet_naming_rule.private_fields_underscore_camelcase.severity = suggestion
-#private_constants_upper_case
+# private_constants_upper_case
dotnet_naming_rule.private_constants_upper_case.symbols = private_field
dotnet_naming_rule.private_constants_upper_case.style = underscore_camel_case
dotnet_naming_rule.private_constants_upper_case.severity = suggestion
+# Enforce file-scoped namespace
+csharp_style_namespace_declarations = file_scoped:error
+dotnet_diagnostic.IDE0161.severity = error
+
diff --git a/src/CodeButler/CodeButler.Console/CodeButler.Console.csproj b/src/CodeButler/CodeButler.Console/CodeButler.Console.csproj
index 8e8cd6f..21f1375 100644
--- a/src/CodeButler/CodeButler.Console/CodeButler.Console.csproj
+++ b/src/CodeButler/CodeButler.Console/CodeButler.Console.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net8.0
CodeButler
enable
true
@@ -10,11 +10,12 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/InputOutputMode.cs b/src/CodeButler/CodeButler.Console/InputOutputMode.cs
new file mode 100644
index 0000000..31e806d
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/InputOutputMode.cs
@@ -0,0 +1,7 @@
+namespace CodeButler;
+
+public enum InputOutputMode
+{
+ Console,
+ File,
+}
diff --git a/src/CodeButler/CodeButler.Console/Mode.cs b/src/CodeButler/CodeButler.Console/Mode.cs
deleted file mode 100644
index b4122dd..0000000
--- a/src/CodeButler/CodeButler.Console/Mode.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace CodeButler
-{
- internal enum Mode
- {
- Console,
- File,
- }
-}
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/Padding/PaddingCleaner.cs b/src/CodeButler/CodeButler.Console/Padding/PaddingCleaner.cs
index 3f357ac..da4a49b 100644
--- a/src/CodeButler/CodeButler.Console/Padding/PaddingCleaner.cs
+++ b/src/CodeButler/CodeButler.Console/Padding/PaddingCleaner.cs
@@ -1,18 +1,22 @@
-using System.Linq;
-using System.Text.RegularExpressions;
+using System.Text.RegularExpressions;
-namespace CodeButler.Padding
+namespace CodeButler.Padding;
+
+public partial class PaddingCleaner
{
- public class PaddingCleaner
- {
- private static readonly Regex _dirtyNewLine = new Regex(@"[\t ]+(\r\n|\n)", RegexOptions.Multiline);
- private static readonly Regex _multiEmptyLine = new Regex(@"^(\r\n|\n)+", RegexOptions.Multiline);
+ private static readonly Regex _dirtyNewLine = GenerateDirtyNewLineRegex();
+ private static readonly Regex _multiEmptyLine = GenerateMultiEmptyLineRegex();
- public string Clean(string input)
- {
- string output = _dirtyNewLine.Replace(input, match => match.Groups.Values.Last().Value);
- output = _multiEmptyLine.Replace(output, match => match.Groups.Values.Last().Value);
- return output;
- }
+ public virtual string Clean(string input)
+ {
+ string output = _dirtyNewLine.Replace(input, match => match.Groups[^1].Value);
+ output = _multiEmptyLine.Replace(output, match => match.Groups[^1].Value);
+ return output;
}
-}
\ No newline at end of file
+
+ [GeneratedRegex(@"[\t ]+(\r\n|\n)", RegexOptions.Multiline)]
+ private static partial Regex GenerateDirtyNewLineRegex();
+
+ [GeneratedRegex(@"^(\r\n|\n)+", RegexOptions.Multiline)]
+ private static partial Regex GenerateMultiEmptyLineRegex();
+}
diff --git a/src/CodeButler/CodeButler.Console/Program.cs b/src/CodeButler/CodeButler.Console/Program.cs
index c872e3f..2265370 100644
--- a/src/CodeButler/CodeButler.Console/Program.cs
+++ b/src/CodeButler/CodeButler.Console/Program.cs
@@ -1,88 +1,48 @@
using System;
+using System.CommandLine;
using System.IO;
using System.Threading.Tasks;
-using CodeButler.Padding;
-using CodeButler.Syntax;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace CodeButler
+namespace CodeButler;
+
+public class Program
{
- public class Program
+ public static async Task Main(string[] args)
{
- public static async Task Main(string[] args)
- {
- Mode mode;
- if (Console.IsInputRedirected)
- {
- mode = Mode.Console;
- }
- else if (args.Length > 0)
- {
- mode = Mode.File;
- }
- else
- {
- Console.Error.WriteLine("No input provided.");
- return -1;
- }
-
- string input = await GetInput(mode, args).ConfigureAwait(false);
- CompilationUnitSyntax root = Parse(input);
- var organizedRoot = Reorganize(root);
-
- await SetOutput(organizedRoot, mode, args).ConfigureAwait(false);
- return 0;
- }
+ var rootCommand = new RootCommand("Reorganises, sorts and cleans up the provided C# file.");
- public static CompilationUnitSyntax Parse(string input)
- {
- var paddingCleaner = new PaddingCleaner();
- string cleanInput = paddingCleaner.Clean(input);
- SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(cleanInput);
- CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot();
- return root;
- }
-
- public static CompilationUnitSyntax Reorganize(CompilationUnitSyntax compilationUnit)
- {
- return compilationUnit.Reorganize();
- }
+ var noSortMemberByAlphabetOption = new Option(
+ name: "--no-sort-member-by-alphabet",
+ description: "Disables sorting members by alphabet.",
+ getDefaultValue: () => false
+ );
- private static async Task GetInput(Mode mode, string[] args)
+ var inputFileArgMeta = new
{
- switch (mode)
- {
- case Mode.Console:
- using (var reader = new StreamReader(Console.OpenStandardInput(), Console.InputEncoding))
- {
- return await reader.ReadToEndAsync().ConfigureAwait(false);
- }
-
- case Mode.File:
- return await File.ReadAllTextAsync(args[0]).ConfigureAwait(false);
-
- default:
- throw new NotImplementedException($"Mode \"{mode}\" not implemented.");
- }
- }
-
- private static async Task SetOutput(CompilationUnitSyntax compilationUnit, Mode mode, string[] args)
- {
- switch (mode)
- {
- case Mode.Console:
- Console.Write(compilationUnit.ToFullString());
- break;
-
- case Mode.File:
- await File.WriteAllTextAsync(args[0], compilationUnit.ToFullString()).ConfigureAwait(false);
- break;
-
- default:
- throw new NotImplementedException($"Mode \"{mode}\" not implemented.");
- }
- }
+ Name = "input",
+ Description = "Path to input file or piped input."
+ };
+
+ var inputFileArg = Console.IsInputRedirected
+ ? new Argument(
+ name: inputFileArgMeta.Name,
+ description: inputFileArgMeta.Description,
+ getDefaultValue: () => null
+ )
+ : new Argument(
+ name: inputFileArgMeta.Name,
+ description: inputFileArgMeta.Description
+ );
+
+ rootCommand.AddOption(noSortMemberByAlphabetOption);
+ rootCommand.AddArgument(inputFileArg);
+
+ rootCommand.SetHandler(
+ RootCommandHandler.Handle,
+ new RootCommandConfigurationBinder(noSortMemberByAlphabetOption, inputFileArg)
+ );
+
+ var exitCode = await rootCommand.InvokeAsync(args);
+ return exitCode;
}
-}
\ No newline at end of file
+}
diff --git a/src/CodeButler/CodeButler.Console/Properties/launchSettings.json b/src/CodeButler/CodeButler.Console/Properties/launchSettings.json
index cbbe867..6fa6939 100644
--- a/src/CodeButler/CodeButler.Console/Properties/launchSettings.json
+++ b/src/CodeButler/CodeButler.Console/Properties/launchSettings.json
@@ -2,7 +2,6 @@
"profiles": {
"CodeButler.Console": {
"commandName": "Project",
- "commandLineArgs": "TypeDefaultOrder.cs"
}
}
}
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberAccessModifier.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberAccessModifier.cs
index a0ae775..7555cbb 100644
--- a/src/CodeButler/CodeButler.Console/Reorganizing/MemberAccessModifier.cs
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/MemberAccessModifier.cs
@@ -1,13 +1,12 @@
-namespace CodeButler.Reorganizing
+namespace CodeButler.Reorganizing;
+
+public enum MemberAccessModifier
{
- public enum MemberAccessModifier
- {
- Public,
- Internal,
- Protected,
- ProtectedInternal,
- PrivateProtected,
- Private,
- None,
- }
-}
\ No newline at end of file
+ Public,
+ Internal,
+ Protected,
+ ProtectedInternal,
+ PrivateProtected,
+ Private,
+ None,
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberAdditionalModifier.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberAdditionalModifier.cs
index 337677a..9d8a250 100644
--- a/src/CodeButler/CodeButler.Console/Reorganizing/MemberAdditionalModifier.cs
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/MemberAdditionalModifier.cs
@@ -1,11 +1,10 @@
-namespace CodeButler.Reorganizing
+namespace CodeButler.Reorganizing;
+
+public enum MemberAdditionalModifier
{
- public enum MemberAdditionalModifier
- {
- Const,
- StaticReadonly,
- Static,
- Readonly,
- None,
- }
-}
\ No newline at end of file
+ Const,
+ StaticReadonly,
+ Static,
+ Readonly,
+ None,
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberInfo.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberInfo.cs
new file mode 100644
index 0000000..7c2e1fa
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/MemberInfo.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+
+namespace CodeButler.Reorganizing;
+
+public class MemberInfo : IEquatable
+{
+ public MemberInfo()
+ {
+ AccessModifier = MemberAccessModifier.None;
+ AdditionalModifier = MemberAdditionalModifier.None;
+ MemberType = MemberType.None;
+ }
+
+ public MemberAccessModifier AccessModifier { get; init; }
+
+ public MemberAdditionalModifier AdditionalModifier { get; init; }
+
+ public string? Identifier { get; init; }
+
+ public MemberType MemberType { get; init; }
+
+ public static bool operator !=(MemberInfo? left, MemberInfo? right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator ==(MemberInfo? left, MemberInfo? right)
+ {
+ return EqualityComparer.Default.Equals(left, right);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as MemberInfo);
+ }
+
+ public bool Equals(MemberInfo? other)
+ {
+ return other != null
+ && MemberType == other.MemberType
+ && Identifier == other.Identifier
+ && AccessModifier == other.AccessModifier
+ && AdditionalModifier == other.AdditionalModifier;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(MemberType, Identifier, AccessModifier, AdditionalModifier);
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberInfoComparer.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberInfoComparer.cs
new file mode 100644
index 0000000..a4807c7
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/MemberInfoComparer.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CodeButler.Reorganizing;
+
+public class MemberInfoComparer : IComparer
+{
+ private readonly MemberSortConfiguration _configuration;
+ private readonly Func[] _compareMethods;
+
+ public MemberInfoComparer(MemberSortConfiguration configuration)
+ {
+ _configuration = configuration;
+ _compareMethods =
+ [
+ CompareByMemberType,
+ CompareByAccessModifier,
+ CompareByAdditionalModifier,
+ CompareByIdentifier,
+ ];
+ }
+
+ public static MemberInfoComparer Default { get; } =
+ new MemberInfoComparer(new MemberSortConfiguration());
+
+ public int Compare(MemberInfo? x, MemberInfo? y)
+ {
+ return _compareMethods
+ .Select(compareMethod => compareMethod(x, y))
+ .FirstOrDefault(result => result != 0);
+ }
+
+ private int CompareByAccessModifier(MemberInfo? x, MemberInfo? y)
+ {
+ return (x, y) switch
+ {
+ (null, null) => 0,
+ (null, _) => -1,
+ (_, null) => 1,
+ _ => x.AccessModifier - y.AccessModifier,
+ };
+ }
+
+ private int CompareByAdditionalModifier(MemberInfo? x, MemberInfo? y)
+ {
+ return (x, y) switch
+ {
+ (null, null) => 0,
+ (null, _) => -1,
+ (_, null) => 1,
+ _ => x.AdditionalModifier - y.AdditionalModifier,
+ };
+ }
+
+ private int CompareByIdentifier(MemberInfo? x, MemberInfo? y)
+ {
+ if (!_configuration.SortByAlphabet)
+ {
+ return 0;
+ }
+
+ return (x, y) switch
+ {
+ (null, null) => 0,
+ (null, _) => -1,
+ (_, null) => 1,
+ _ => string.Compare(x.Identifier, y.Identifier, StringComparison.Ordinal),
+ };
+ }
+
+ private int CompareByMemberType(MemberInfo? x, MemberInfo? y)
+ {
+ return (x, y) switch
+ {
+ (null, null) => 0,
+ (null, _) => -1,
+ (_, null) => 1,
+ _ => x.MemberType - y.MemberType,
+ };
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberOrderInfo.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberOrderInfo.cs
deleted file mode 100644
index 43ee449..0000000
--- a/src/CodeButler/CodeButler.Console/Reorganizing/MemberOrderInfo.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace CodeButler.Reorganizing
-{
- public class MemberOrderInfo : IComparable, IEquatable
- {
- private readonly Func[] _compareMethods;
-
- public MemberOrderInfo()
- {
- AccessModifier = MemberAccessModifier.None;
- AdditionalModifier = MemberAdditionalModifier.None;
- MemberType = MemberType.None;
-
- _compareMethods = new Func[]
- {
- CompareByMemberType,
- CompareByAccessModifier,
- CompareByAdditionalModifier,
- CompareByIdentifier,
- };
- }
-
- public MemberAccessModifier AccessModifier { get; set; }
-
- public MemberAdditionalModifier AdditionalModifier { get; set; }
-
- public string? Identifier { get; set; }
-
- public MemberType MemberType { get; set; }
-
- public static bool operator !=(MemberOrderInfo? left, MemberOrderInfo? right)
- {
- return !(left == right);
- }
-
- public static bool operator <(MemberOrderInfo left, MemberOrderInfo right)
- {
- return left is null ? right is not null : left.CompareTo(right) < 0;
- }
-
- public static bool operator <=(MemberOrderInfo left, MemberOrderInfo right)
- {
- return left is null || left.CompareTo(right) <= 0;
- }
-
- public static bool operator ==(MemberOrderInfo? left, MemberOrderInfo? right)
- {
- return EqualityComparer.Default.Equals(left, right);
- }
-
- public static bool operator >(MemberOrderInfo left, MemberOrderInfo right)
- {
- return left is not null && left.CompareTo(right) > 0;
- }
-
- public static bool operator >=(MemberOrderInfo left, MemberOrderInfo right)
- {
- return left is null ? right is null : left.CompareTo(right) >= 0;
- }
-
- public int CompareByAccessModifier(MemberOrderInfo? other)
- {
- return other is null ? -1 : AccessModifier - other.AccessModifier;
- }
-
- public int CompareByAdditionalModifier(MemberOrderInfo? other)
- {
- return other is null ? -1 : AdditionalModifier - other.AdditionalModifier;
- }
-
- public int CompareByIdentifier(MemberOrderInfo? other)
- {
- return (other is null) ? -1 : string.Compare(Identifier, other.Identifier, StringComparison.Ordinal);
- }
-
- public int CompareByMemberType(MemberOrderInfo? other)
- {
- return other is null ? -1 : MemberType - other.MemberType;
- }
-
- public int CompareTo(MemberOrderInfo? other)
- {
- return _compareMethods
- .Select(compareMethod => compareMethod(other))
- .FirstOrDefault(result => result != 0);
- }
-
- public override bool Equals(object? obj)
- {
- return Equals(obj as MemberOrderInfo);
- }
-
- public bool Equals(MemberOrderInfo? other)
- {
- return other != null &&
- MemberType == other.MemberType &&
- Identifier == other.Identifier &&
- AccessModifier == other.AccessModifier &&
- AdditionalModifier == other.AdditionalModifier;
- }
-
- public override int GetHashCode()
- {
- return HashCode.Combine(MemberType, Identifier, AccessModifier, AdditionalModifier);
- }
- }
-}
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberSortConfiguration.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberSortConfiguration.cs
new file mode 100644
index 0000000..c3818ec
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/MemberSortConfiguration.cs
@@ -0,0 +1,6 @@
+namespace CodeButler.Reorganizing;
+
+public class MemberSortConfiguration
+{
+ public bool SortByAlphabet { get; init; } = true;
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/MemberType.cs b/src/CodeButler/CodeButler.Console/Reorganizing/MemberType.cs
index 105021c..a24e323 100644
--- a/src/CodeButler/CodeButler.Console/Reorganizing/MemberType.cs
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/MemberType.cs
@@ -1,20 +1,19 @@
-namespace CodeButler.Reorganizing
+namespace CodeButler.Reorganizing;
+
+public enum MemberType
{
- public enum MemberType
- {
- Field,
- Constructor,
- Destructor,
- Delegate,
- Event,
- Enum,
- Interface,
- Property,
- Indexer,
- Operator,
- Method,
- Struct,
- Class,
- None,
- }
-}
\ No newline at end of file
+ Field,
+ Constructor,
+ Destructor,
+ Delegate,
+ Event,
+ Enum,
+ Interface,
+ Property,
+ Indexer,
+ Operator,
+ Method,
+ Struct,
+ Class,
+ None,
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/UsingInfo.cs b/src/CodeButler/CodeButler.Console/Reorganizing/UsingInfo.cs
new file mode 100644
index 0000000..75ec23d
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/Reorganizing/UsingInfo.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace CodeButler.Reorganizing;
+
+public sealed class UsingInfo : IEquatable, IComparable
+{
+ private const string _systemUsing = "System";
+
+ private readonly Func[] _compareMethods;
+
+ public UsingInfo(string name)
+ {
+ Name = name ?? throw new ArgumentNullException(nameof(name));
+ _compareMethods = new Func[]
+ {
+ CompareByIsGlobal,
+ CompareByIsStatic,
+ CompareByAlias,
+ CompareByName,
+ };
+ }
+
+ public string? Alias { get; set; }
+
+ public bool IsStatic { get; set; }
+ public bool IsGlobal { get; set; }
+
+ public string Name { get; }
+
+ public static bool operator !=(UsingInfo? left, UsingInfo? right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator <(UsingInfo left, UsingInfo right)
+ {
+ return left is null ? right is not null : left.CompareTo(right) < 0;
+ }
+
+ public static bool operator <=(UsingInfo left, UsingInfo right)
+ {
+ return left is null || left.CompareTo(right) <= 0;
+ }
+
+ public static bool operator ==(UsingInfo? left, UsingInfo? right)
+ {
+ return EqualityComparer.Default.Equals(left, right);
+ }
+
+ public static bool operator >(UsingInfo left, UsingInfo right)
+ {
+ return left is not null && left.CompareTo(right) > 0;
+ }
+
+ public static bool operator >=(UsingInfo left, UsingInfo right)
+ {
+ return left is null ? right is null : left.CompareTo(right) >= 0;
+ }
+
+ public int CompareByAlias(UsingInfo? other)
+ {
+ if (other is null)
+ {
+ return -1;
+ }
+
+ return string.Compare(Alias, other.Alias, StringComparison.Ordinal);
+ }
+
+ public int CompareByIsStatic(UsingInfo? other)
+ {
+ if (other is null)
+ {
+ return -1;
+ }
+
+ return (IsStatic, other.IsStatic) switch
+ {
+ (false, true) => -1,
+ (true, false) => 1,
+ (_, _) => 0,
+ };
+ }
+
+ public int CompareByIsGlobal(UsingInfo? other)
+ {
+ if (other is null)
+ {
+ return -1;
+ }
+
+ return (IsGlobal, other.IsGlobal) switch
+ {
+ (true, false) => -1,
+ (false, true) => 1,
+ (_, _) => 0,
+ };
+ }
+
+ public int CompareByName(UsingInfo? other)
+ {
+ if (other is null)
+ {
+ return -1;
+ }
+
+ return (IsSystemUsing(this), IsSystemUsing(other)) switch
+ {
+ (true, false) => -1,
+ (false, true) => 1,
+ (_, _) => string.Compare(Name, other.Name, StringComparison.Ordinal),
+ };
+ }
+
+ public int CompareTo(UsingInfo? other)
+ {
+ return _compareMethods
+ .Select(compareMethod => compareMethod(other))
+ .FirstOrDefault(result => result != 0);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as UsingInfo);
+ }
+
+ public bool Equals(UsingInfo? other)
+ {
+ return other != null && Name == other.Name;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Name);
+ }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ if (IsGlobal)
+ {
+ sb.Append("global ");
+ }
+
+ sb.Append("using");
+
+ if (!string.IsNullOrEmpty(Alias))
+ {
+ sb.Append($" {Alias} =");
+ }
+
+ if (IsStatic)
+ {
+ sb.Append(" static");
+ }
+
+ sb.Append($" {Name};");
+
+ return sb.ToString();
+ }
+
+ private static bool IsSystemUsing(UsingInfo usingOrderInfo)
+ {
+ return usingOrderInfo.Name.StartsWith(_systemUsing, StringComparison.Ordinal);
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Reorganizing/UsingOrderInfo.cs b/src/CodeButler/CodeButler.Console/Reorganizing/UsingOrderInfo.cs
deleted file mode 100644
index 76bb41d..0000000
--- a/src/CodeButler/CodeButler.Console/Reorganizing/UsingOrderInfo.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace CodeButler.Reorganizing
-{
- public sealed class UsingOrderInfo : IEquatable, IComparable
- {
- private const string _systemUsing = "System";
-
- private readonly Func[] _compareMethods;
-
- public UsingOrderInfo(string name)
- {
- Name = name ?? throw new ArgumentNullException(nameof(name));
- _compareMethods = new Func[]
- {
- CompareByIsGlobal,
- CompareByIsStatic,
- CompareByAlias,
- CompareByName,
- };
- }
-
- public string? Alias { get; set; }
-
- public bool IsStatic { get; set; }
- public bool IsGlobal { get; set; }
-
- public string Name { get; }
-
- public static bool operator !=(UsingOrderInfo? left, UsingOrderInfo? right)
- {
- return !(left == right);
- }
-
- public static bool operator <(UsingOrderInfo left, UsingOrderInfo right)
- {
- return left is null ? right is not null : left.CompareTo(right) < 0;
- }
-
- public static bool operator <=(UsingOrderInfo left, UsingOrderInfo right)
- {
- return left is null || left.CompareTo(right) <= 0;
- }
-
- public static bool operator ==(UsingOrderInfo? left, UsingOrderInfo? right)
- {
- return EqualityComparer.Default.Equals(left, right);
- }
-
- public static bool operator >(UsingOrderInfo left, UsingOrderInfo right)
- {
- return left is not null && left.CompareTo(right) > 0;
- }
-
- public static bool operator >=(UsingOrderInfo left, UsingOrderInfo right)
- {
- return left is null ? right is null : left.CompareTo(right) >= 0;
- }
-
- public int CompareByAlias(UsingOrderInfo? other)
- {
- if (other is null)
- {
- return -1;
- }
-
- return string.Compare(Alias, other.Alias, StringComparison.Ordinal);
- }
-
- public int CompareByIsStatic(UsingOrderInfo? other)
- {
- if (other is null)
- {
- return -1;
- }
-
- return (IsStatic, other.IsStatic) switch
- {
- (false, true) => -1,
- (true, false) => 1,
- (_, _) => 0,
- };
- }
-
- public int CompareByIsGlobal(UsingOrderInfo? other)
- {
- if (other is null)
- {
- return -1;
- }
-
- return (IsGlobal, other.IsGlobal) switch
- {
- (true, false) => -1,
- (false, true) => 1,
- (_, _) => 0,
- };
- }
-
- public int CompareByName(UsingOrderInfo? other)
- {
- if (other is null)
- {
- return -1;
- }
-
- return (IsSystemUsing(this), IsSystemUsing(other)) switch
- {
- (true, false) => -1,
- (false, true) => 1,
- (_, _) => string.Compare(Name, other.Name, StringComparison.Ordinal),
- };
- }
-
- public int CompareTo(UsingOrderInfo? other)
- {
- return _compareMethods
- .Select(compareMethod => compareMethod(other))
- .FirstOrDefault(result => result != 0);
- }
-
- public override bool Equals(object? obj)
- {
- return Equals(obj as UsingOrderInfo);
- }
-
- public bool Equals(UsingOrderInfo? other)
- {
- return other != null &&
- Name == other.Name;
- }
-
- public override int GetHashCode()
- {
- return HashCode.Combine(Name);
- }
-
- public override string ToString()
- {
- var sb = new StringBuilder();
-
- if (IsGlobal)
- {
- sb.Append("global ");
- }
-
- sb.Append("using");
-
- if (!string.IsNullOrEmpty(Alias))
- {
- sb.Append($" {Alias} =");
- }
-
- if (IsStatic)
- {
- sb.Append(" static");
- }
-
- sb.Append($" {Name};");
-
- return sb.ToString();
- }
-
- private static bool IsSystemUsing(UsingOrderInfo usingOrderInfo)
- {
- return usingOrderInfo.Name.StartsWith(_systemUsing, StringComparison.Ordinal);
- }
- }
-}
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/RootCommandConfiguration.cs b/src/CodeButler/CodeButler.Console/RootCommandConfiguration.cs
new file mode 100644
index 0000000..509e234
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/RootCommandConfiguration.cs
@@ -0,0 +1,12 @@
+using System.CommandLine;
+using System.IO;
+
+namespace CodeButler;
+
+public class RootCommandConfiguration
+{
+ public bool SortMemberByAlphabet { get; init; }
+ public InputOutputMode Mode { get; init; }
+ public FileInfo? File { get; init; }
+ public IConsole Console { get; init; } = null!;
+}
diff --git a/src/CodeButler/CodeButler.Console/RootCommandConfigurationBinder.cs b/src/CodeButler/CodeButler.Console/RootCommandConfigurationBinder.cs
new file mode 100644
index 0000000..1434825
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/RootCommandConfigurationBinder.cs
@@ -0,0 +1,38 @@
+using System.CommandLine;
+using System.CommandLine.Binding;
+using System.IO;
+
+namespace CodeButler;
+
+public class RootCommandConfigurationBinder : BinderBase
+{
+ private readonly Option _noSortMemberByAlphabet;
+ private readonly Argument _file;
+
+ public RootCommandConfigurationBinder(
+ Option noSortMemberByAlphabet,
+ Argument file
+ )
+ {
+ _noSortMemberByAlphabet = noSortMemberByAlphabet;
+ _file = file;
+ }
+
+ protected override RootCommandConfiguration GetBoundValue(BindingContext bindingContext)
+ {
+ var parseResult = bindingContext.ParseResult;
+
+ var file = parseResult.GetValueForArgument(_file);
+ var mode = file is null ? InputOutputMode.Console : InputOutputMode.File;
+
+ return new RootCommandConfiguration
+ {
+ SortMemberByAlphabet = !bindingContext.ParseResult.GetValueForOption(
+ _noSortMemberByAlphabet
+ ),
+ Mode = mode,
+ File = file,
+ Console = bindingContext.Console,
+ };
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/RootCommandHandler.cs b/src/CodeButler/CodeButler.Console/RootCommandHandler.cs
new file mode 100644
index 0000000..3681ae9
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/RootCommandHandler.cs
@@ -0,0 +1,108 @@
+using System;
+using System.CommandLine;
+using System.IO;
+using System.Threading.Tasks;
+using CodeButler.Padding;
+using CodeButler.Reorganizing;
+using CodeButler.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace CodeButler;
+
+public class RootCommandHandler
+{
+ public static async Task Handle(RootCommandConfiguration configuration)
+ {
+ var input = await ReadInput(configuration).ConfigureAwait(false);
+
+ var clean = Clean(input);
+ var root = Parse(clean);
+ var organizedRoot = Reorganize(root, configuration);
+
+ await WriteOutput(organizedRoot, configuration).ConfigureAwait(false);
+ }
+
+ private static string Clean(string input)
+ {
+ var paddingCleaner = new PaddingCleaner();
+ return paddingCleaner.Clean(input);
+ }
+
+ private static CompilationUnitSyntax Parse(string input)
+ {
+ var paddingCleaner = new PaddingCleaner();
+ var cleanInput = paddingCleaner.Clean(input);
+ var syntaxTree = CSharpSyntaxTree.ParseText(cleanInput);
+ var root = syntaxTree.GetCompilationUnitRoot();
+ return root;
+ }
+
+ private static CompilationUnitSyntax Reorganize(
+ CompilationUnitSyntax compilationUnit,
+ RootCommandConfiguration configuration
+ )
+ {
+ var comparer = new MemberInfoComparer(
+ new MemberSortConfiguration { SortByAlphabet = configuration.SortMemberByAlphabet }
+ );
+
+ var organizer = new SyntaxReorganizerRewriter(comparer);
+ return compilationUnit.Accept(organizer) as CompilationUnitSyntax
+ ?? throw new Exception(
+ $"Reorganized root is null or not of type {typeof(CompilationUnitSyntax)}."
+ );
+ }
+
+ private static async Task ReadInput(RootCommandConfiguration configuration)
+ {
+ switch (configuration.Mode)
+ {
+ case InputOutputMode.Console:
+ using (
+ var reader = new StreamReader(
+ Console.OpenStandardInput(),
+ Console.InputEncoding
+ )
+ )
+ {
+ return await reader.ReadToEndAsync().ConfigureAwait(false);
+ }
+
+ case InputOutputMode.File:
+ return await File.ReadAllTextAsync(configuration.File!.FullName)
+ .ConfigureAwait(false);
+
+ default:
+ throw new NotImplementedException(
+ $"Mode \"{configuration.Mode}\" not implemented."
+ );
+ }
+ }
+
+ private static async Task WriteOutput(
+ CompilationUnitSyntax compilationUnit,
+ RootCommandConfiguration configuration
+ )
+ {
+ switch (configuration.Mode)
+ {
+ case InputOutputMode.Console:
+ configuration.Console.Write(compilationUnit.ToFullString());
+ break;
+
+ case InputOutputMode.File:
+ await File.WriteAllTextAsync(
+ configuration.File!.FullName,
+ compilationUnit.ToFullString()
+ )
+ .ConfigureAwait(false);
+ break;
+
+ default:
+ throw new NotImplementedException(
+ $"Mode \"{configuration.Mode}\" not implemented."
+ );
+ }
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Syntax/MemberInfoFactory.cs b/src/CodeButler/CodeButler.Console/Syntax/MemberInfoFactory.cs
new file mode 100644
index 0000000..5c59ce1
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/Syntax/MemberInfoFactory.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Linq;
+using CodeButler.Reorganizing;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace CodeButler.Syntax;
+
+public static class MemberInfoFactory
+{
+ public static MemberInfo GetMemberInfo(this MemberDeclarationSyntax memberDeclaration)
+ {
+ ArgumentNullException.ThrowIfNull(memberDeclaration);
+
+ return new MemberInfo()
+ {
+ Identifier = GetMemberName(memberDeclaration),
+ AccessModifier = GetMemberAccessModifier(memberDeclaration),
+ AdditionalModifier = GetMemberAdditionalModifier(memberDeclaration),
+ MemberType = GetMemberType(memberDeclaration),
+ };
+ }
+
+ private static string GetMemberName(MemberDeclarationSyntax memberDeclaration)
+ {
+ var name = memberDeclaration switch
+ {
+ NamespaceDeclarationSyntax declaration => declaration.Name.ToString(),
+ FieldDeclarationSyntax declaration
+ => string.Join(
+ ", ",
+ declaration.Declaration.Variables.Select(variableDeclaration =>
+ variableDeclaration.Identifier
+ )
+ ),
+ DelegateDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ EventDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ EnumDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ InterfaceDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ PropertyDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ MethodDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ StructDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ ClassDeclarationSyntax declaration => declaration.Identifier.ToString(),
+ _ => string.Empty,
+ };
+
+ return name;
+ }
+
+ private static MemberType GetMemberType(MemberDeclarationSyntax memberDeclaration)
+ {
+ SyntaxKind kind = memberDeclaration.Kind();
+ return kind switch
+ {
+ SyntaxKind.FieldDeclaration => MemberType.Field,
+ SyntaxKind.ConstructorDeclaration => MemberType.Constructor,
+ SyntaxKind.DestructorDeclaration => MemberType.Destructor,
+ SyntaxKind.DelegateDeclaration => MemberType.Delegate,
+ SyntaxKind.EventDeclaration => MemberType.Event,
+ SyntaxKind.EventFieldDeclaration => MemberType.Event,
+ SyntaxKind.EnumDeclaration => MemberType.Enum,
+ SyntaxKind.InterfaceDeclaration => MemberType.Interface,
+ SyntaxKind.PropertyDeclaration => MemberType.Property,
+ SyntaxKind.IndexerDeclaration => MemberType.Indexer,
+ SyntaxKind.OperatorDeclaration => MemberType.Indexer,
+ SyntaxKind.MethodDeclaration => MemberType.Method,
+ SyntaxKind.StructDeclaration => MemberType.Struct,
+ SyntaxKind.ClassDeclaration => MemberType.Class,
+ _ => MemberType.None,
+ };
+ }
+
+ private static MemberAccessModifier GetMemberAccessModifier(
+ MemberDeclarationSyntax memberDeclaration
+ )
+ {
+ var modifierKinds = memberDeclaration.Modifiers.Select(token => token.Kind()).ToHashSet();
+
+ if (modifierKinds.Count == 0)
+ {
+ return MemberAccessModifier.None;
+ }
+ else if (modifierKinds.Contains(SyntaxKind.PublicKeyword))
+ {
+ // public
+ return MemberAccessModifier.Public;
+ }
+ else if (modifierKinds.Contains(SyntaxKind.InternalKeyword))
+ {
+ // (?) internal
+ if (modifierKinds.Contains(SyntaxKind.ProtectedKeyword))
+ {
+ // protected internal
+ return MemberAccessModifier.ProtectedInternal;
+ }
+ else
+ {
+ // internal
+ return MemberAccessModifier.Internal;
+ }
+ }
+ else if (modifierKinds.Contains(SyntaxKind.ProtectedKeyword))
+ {
+ // (?) protected (?)
+ if (modifierKinds.Contains(SyntaxKind.InternalKeyword))
+ {
+ // protected internal
+ return MemberAccessModifier.ProtectedInternal;
+ }
+ else if (modifierKinds.Contains(SyntaxKind.PrivateKeyword))
+ {
+ // private protected
+ return MemberAccessModifier.PrivateProtected;
+ }
+ else
+ {
+ // protected
+ return MemberAccessModifier.Protected;
+ }
+ }
+ else if (modifierKinds.Contains(SyntaxKind.PrivateKeyword))
+ {
+ return MemberAccessModifier.Private;
+ }
+ else
+ {
+ return MemberAccessModifier.None;
+ }
+ }
+
+ private static MemberAdditionalModifier GetMemberAdditionalModifier(
+ MemberDeclarationSyntax memberDeclaration
+ )
+ {
+ var modifierKinds = memberDeclaration.Modifiers.Select(token => token.Kind()).ToHashSet();
+
+ if (modifierKinds.Count == 0)
+ {
+ return MemberAdditionalModifier.None;
+ }
+ else if (modifierKinds.Contains(SyntaxKind.ConstKeyword))
+ {
+ return MemberAdditionalModifier.Const;
+ }
+ else if (
+ modifierKinds.Contains(SyntaxKind.StaticKeyword)
+ && modifierKinds.Contains(SyntaxKind.ReadOnlyKeyword)
+ )
+ {
+ return MemberAdditionalModifier.StaticReadonly;
+ }
+ else if (modifierKinds.Contains(SyntaxKind.ReadOnlyKeyword))
+ {
+ return MemberAdditionalModifier.Readonly;
+ }
+ else if (modifierKinds.Contains(SyntaxKind.StaticKeyword))
+ {
+ return MemberAdditionalModifier.Static;
+ }
+ else
+ {
+ return MemberAdditionalModifier.None;
+ }
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Syntax/MemberOrderInfoExtensions.cs b/src/CodeButler/CodeButler.Console/Syntax/MemberOrderInfoExtensions.cs
deleted file mode 100644
index 664a0dc..0000000
--- a/src/CodeButler/CodeButler.Console/Syntax/MemberOrderInfoExtensions.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using System;
-using System.Linq;
-using CodeButler.Reorganizing;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-
-namespace CodeButler.Syntax
-{
- public static class MemberOrderInfoExtensions
- {
- public static MemberOrderInfo GetMemberOrderInfo(this MemberDeclarationSyntax memberDeclaration)
- {
- if (memberDeclaration is null)
- {
- throw new ArgumentNullException(nameof(memberDeclaration));
- }
-
- return new MemberOrderInfo()
- {
- Identifier = GetMemberName(memberDeclaration),
- AccessModifier = GetMemberAccessModifier(memberDeclaration),
- AdditionalModifier = GetMemberAdditionalModifier(memberDeclaration),
- MemberType = GetMemberType(memberDeclaration),
- };
- }
-
- private static string GetMemberName(MemberDeclarationSyntax memberDeclaration)
- {
- var name = memberDeclaration switch
- {
- NamespaceDeclarationSyntax declaration => declaration.Name.ToString(),
- FieldDeclarationSyntax declaration => string.Join(", ", declaration.Declaration.Variables.Select(variableDeclaration => variableDeclaration.Identifier)),
- DelegateDeclarationSyntax declaration => declaration.Identifier.ToString(),
- EventDeclarationSyntax declaration => declaration.Identifier.ToString(),
- EnumDeclarationSyntax declaration => declaration.Identifier.ToString(),
- InterfaceDeclarationSyntax declaration => declaration.Identifier.ToString(),
- PropertyDeclarationSyntax declaration => declaration.Identifier.ToString(),
- MethodDeclarationSyntax declaration => declaration.Identifier.ToString(),
- StructDeclarationSyntax declaration => declaration.Identifier.ToString(),
- ClassDeclarationSyntax declaration => declaration.Identifier.ToString(),
- _ => string.Empty,
- };
-
- return name;
- }
-
- private static MemberType GetMemberType(MemberDeclarationSyntax memberDeclaration)
- {
- SyntaxKind kind = memberDeclaration.Kind();
- return kind switch
- {
- SyntaxKind.FieldDeclaration => MemberType.Field,
- SyntaxKind.ConstructorDeclaration => MemberType.Constructor,
- SyntaxKind.DestructorDeclaration => MemberType.Destructor,
- SyntaxKind.DelegateDeclaration => MemberType.Delegate,
- SyntaxKind.EventDeclaration => MemberType.Event,
- SyntaxKind.EventFieldDeclaration => MemberType.Event,
- SyntaxKind.EnumDeclaration => MemberType.Enum,
- SyntaxKind.InterfaceDeclaration => MemberType.Interface,
- SyntaxKind.PropertyDeclaration => MemberType.Property,
- SyntaxKind.IndexerDeclaration => MemberType.Indexer,
- SyntaxKind.OperatorDeclaration => MemberType.Indexer,
- SyntaxKind.MethodDeclaration => MemberType.Method,
- SyntaxKind.StructDeclaration => MemberType.Struct,
- SyntaxKind.ClassDeclaration => MemberType.Class,
- _ => MemberType.None,
- };
- }
-
- private static MemberAccessModifier GetMemberAccessModifier(MemberDeclarationSyntax memberDeclaration)
- {
- var modifierKinds = memberDeclaration.Modifiers
- .Select(token => token.Kind())
- .ToHashSet();
-
- if (modifierKinds.Count == 0)
- {
- return MemberAccessModifier.None;
- }
- else if (modifierKinds.Contains(SyntaxKind.PublicKeyword))
- {
- // public
- return MemberAccessModifier.Public;
- }
- else if (modifierKinds.Contains(SyntaxKind.InternalKeyword))
- {
- // (?) internal
- if (modifierKinds.Contains(SyntaxKind.ProtectedKeyword))
- {
- // protected internal
- return MemberAccessModifier.ProtectedInternal;
- }
- else
- {
- // internal
- return MemberAccessModifier.Internal;
- }
- }
- else if (modifierKinds.Contains(SyntaxKind.ProtectedKeyword))
- {
- // (?) protected (?)
- if (modifierKinds.Contains(SyntaxKind.InternalKeyword))
- {
- // protected internal
- return MemberAccessModifier.ProtectedInternal;
- }
- else if (modifierKinds.Contains(SyntaxKind.PrivateKeyword))
- {
- // private protected
- return MemberAccessModifier.PrivateProtected;
- }
- else
- {
- // protected
- return MemberAccessModifier.Protected;
- }
- }
- else if (modifierKinds.Contains(SyntaxKind.PrivateKeyword))
- {
- return MemberAccessModifier.Private;
- }
- else
- {
- return MemberAccessModifier.None;
- }
- }
-
- private static MemberAdditionalModifier GetMemberAdditionalModifier(MemberDeclarationSyntax memberDeclaration)
- {
- var modifierKinds = memberDeclaration.Modifiers
- .Select(token => token.Kind())
- .ToHashSet();
-
- if (modifierKinds.Count == 0)
- {
- return MemberAdditionalModifier.None;
- }
- else if (modifierKinds.Contains(SyntaxKind.ConstKeyword))
- {
- return MemberAdditionalModifier.Const;
- }
- else if (modifierKinds.Contains(SyntaxKind.StaticKeyword) && modifierKinds.Contains(SyntaxKind.ReadOnlyKeyword))
- {
- return MemberAdditionalModifier.StaticReadonly;
- }
- else if (modifierKinds.Contains(SyntaxKind.ReadOnlyKeyword))
- {
- return MemberAdditionalModifier.Readonly;
- }
- else if (modifierKinds.Contains(SyntaxKind.StaticKeyword))
- {
- return MemberAdditionalModifier.Static;
- }
- else
- {
- return MemberAdditionalModifier.None;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/Syntax/MembersOrganizerExtensions.cs b/src/CodeButler/CodeButler.Console/Syntax/MembersOrganizerExtensions.cs
deleted file mode 100644
index d7ec7bc..0000000
--- a/src/CodeButler/CodeButler.Console/Syntax/MembersOrganizerExtensions.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-
-namespace CodeButler.Syntax
-{
- public static class MembersOrganizerExtensions
- {
- public static CompilationUnitSyntax Reorganize(this CompilationUnitSyntax root)
- {
- var organizer = new SyntaxReorganizerRewriter();
- return root.Accept(organizer) is CompilationUnitSyntax reorganizedRoot
- ? reorganizedRoot
- : throw new System.Exception("Reorganized root is null or not of type CompilationUnitSyntax.");
- }
- }
-}
\ No newline at end of file
diff --git a/src/CodeButler/CodeButler.Console/Syntax/SyntaxListExtensions.cs b/src/CodeButler/CodeButler.Console/Syntax/SyntaxListExtensions.cs
index 3e192db..8590054 100644
--- a/src/CodeButler/CodeButler.Console/Syntax/SyntaxListExtensions.cs
+++ b/src/CodeButler/CodeButler.Console/Syntax/SyntaxListExtensions.cs
@@ -1,19 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using CodeButler.Reorganizing;
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace CodeButler.Syntax
+namespace CodeButler.Syntax;
+
+public static class SyntaxListExtensions
{
- public static class SyntaxListExtensions
+ public static SyntaxList ToSyntaxList(this IEnumerable nodes)
+ where TNode : SyntaxNode
{
- public static SyntaxList ToSyntaxList(this IEnumerable nodes)
- where TNode : SyntaxNode
- {
- return new SyntaxList(nodes);
- }
+ return new SyntaxList(nodes);
}
-}
\ No newline at end of file
+}
diff --git a/src/CodeButler/CodeButler.Console/Syntax/SyntaxReorganizerRewriter.cs b/src/CodeButler/CodeButler.Console/Syntax/SyntaxReorganizerRewriter.cs
index a9a281f..f16d0e6 100644
--- a/src/CodeButler/CodeButler.Console/Syntax/SyntaxReorganizerRewriter.cs
+++ b/src/CodeButler/CodeButler.Console/Syntax/SyntaxReorganizerRewriter.cs
@@ -1,59 +1,78 @@
using System.Collections.Generic;
using System.Linq;
+using CodeButler.Reorganizing;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace CodeButler.Syntax
+namespace CodeButler.Syntax;
+
+public class SyntaxReorganizerRewriter : CSharpSyntaxRewriter
{
- public class SyntaxReorganizerRewriter : CSharpSyntaxRewriter
- {
- public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
- {
- var members = OrganizeMembers(node.Members);
- return node.WithMembers(members);
- }
-
- public override SyntaxNode? VisitCompilationUnit(CompilationUnitSyntax node)
- {
- var usings = OrganizeUsings(node.Usings);
- var members = OrganizeMembers(node.Members);
- return node.WithUsings(usings).WithMembers(members);
- }
-
- public override SyntaxNode? VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
- {
- var members = OrganizeMembers(node.Members);
- return node.WithMembers(members);
- }
-
- public override SyntaxNode? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
- {
- var usings = OrganizeUsings(node.Usings);
- var members = OrganizeMembers(node.Members);
- return node.WithUsings(usings).WithMembers(members);
- }
-
- public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node)
- {
- var members = OrganizeMembers(node.Members);
- return node.WithMembers(members);
- }
-
- private static SyntaxList OrganizeUsings(IEnumerable usingDirectives)
- {
- var sorted = usingDirectives.OrderBy(UsingOrderInfoExtensions.GetUsingOrderInfo)
- .ToSyntaxList();
- return sorted;
- }
-
- private SyntaxList OrganizeMembers(IEnumerable memberDeclarations)
- {
- return memberDeclarations
- .Select(member => member.Accept(this))
- .OfType()
- .OrderBy(MemberOrderInfoExtensions.GetMemberOrderInfo)
- .ToSyntaxList();
- }
- }
-}
\ No newline at end of file
+ private readonly IComparer _memberInfoComparer;
+
+ public SyntaxReorganizerRewriter(IComparer memberInfoComparer)
+ {
+ _memberInfoComparer = memberInfoComparer;
+ }
+
+ public override SyntaxNode? VisitCompilationUnit(CompilationUnitSyntax node)
+ {
+ var usings = OrganizeUsings(node.Usings);
+ var members = OrganizeMembers(node.Members);
+ return node.WithUsings(usings).WithMembers(members);
+ }
+
+ public override SyntaxNode? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
+ {
+ var usings = OrganizeUsings(node.Usings);
+ var members = OrganizeMembers(node.Members);
+ return node.WithUsings(usings).WithMembers(members);
+ }
+
+ public override SyntaxNode? VisitFileScopedNamespaceDeclaration(
+ FileScopedNamespaceDeclarationSyntax node
+ )
+ {
+ var usings = OrganizeUsings(node.Usings);
+ var members = OrganizeMembers(node.Members);
+ return node.WithUsings(usings).WithMembers(members);
+ }
+
+ public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
+ {
+ var members = OrganizeMembers(node.Members);
+ return node.WithMembers(members);
+ }
+
+ public override SyntaxNode? VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
+ {
+ var members = OrganizeMembers(node.Members);
+ return node.WithMembers(members);
+ }
+
+ public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node)
+ {
+ var members = OrganizeMembers(node.Members);
+ return node.WithMembers(members);
+ }
+
+ private static SyntaxList OrganizeUsings(
+ IEnumerable usingDirectives
+ )
+ {
+ var sorted = usingDirectives.OrderBy(UsingInfoFactory.GetUsingInfo).ToSyntaxList();
+ return sorted;
+ }
+
+ private SyntaxList OrganizeMembers(
+ IEnumerable memberDeclarations
+ )
+ {
+ return memberDeclarations
+ .Select(member => member.Accept(this))
+ .OfType()
+ .OrderBy(MemberInfoFactory.GetMemberInfo, _memberInfoComparer)
+ .ToSyntaxList();
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Syntax/UsingInfoFactory.cs b/src/CodeButler/CodeButler.Console/Syntax/UsingInfoFactory.cs
new file mode 100644
index 0000000..642fa4d
--- /dev/null
+++ b/src/CodeButler/CodeButler.Console/Syntax/UsingInfoFactory.cs
@@ -0,0 +1,17 @@
+using CodeButler.Reorganizing;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace CodeButler.Syntax;
+
+public static class UsingInfoFactory
+{
+ public static UsingInfo GetUsingInfo(this UsingDirectiveSyntax usingDirective)
+ {
+ return new UsingInfo(usingDirective.Name?.ToString() ?? string.Empty)
+ {
+ Alias = usingDirective.Alias?.Name.ToString(),
+ IsStatic = usingDirective.StaticKeyword != default,
+ IsGlobal = usingDirective.GlobalKeyword != default,
+ };
+ }
+}
diff --git a/src/CodeButler/CodeButler.Console/Syntax/UsingOrderInfoExtensions.cs b/src/CodeButler/CodeButler.Console/Syntax/UsingOrderInfoExtensions.cs
deleted file mode 100644
index 40a8f98..0000000
--- a/src/CodeButler/CodeButler.Console/Syntax/UsingOrderInfoExtensions.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using CodeButler.Reorganizing;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-
-namespace CodeButler.Syntax
-{
- public static class UsingOrderInfoExtensions
- {
- public static UsingOrderInfo GetUsingOrderInfo(this UsingDirectiveSyntax usingDirective)
- {
- return new UsingOrderInfo(usingDirective.Name.ToString())
- {
- Alias = usingDirective.Alias?.Name.ToString(),
- IsStatic = usingDirective.StaticKeyword != default,
- IsGlobal = usingDirective.GlobalKeyword != default,
- };
- }
- }
-}
\ No newline at end of file
diff --git a/src/CodeButler/tests/CodeButler.IntegrationTests/CodeButler.IntegrationTests.csproj b/src/CodeButler/tests/CodeButler.IntegrationTests/CodeButler.IntegrationTests.csproj
index 9f6a6cd..2aa9753 100644
--- a/src/CodeButler/tests/CodeButler.IntegrationTests/CodeButler.IntegrationTests.csproj
+++ b/src/CodeButler/tests/CodeButler.IntegrationTests/CodeButler.IntegrationTests.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
@@ -14,6 +14,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
all
diff --git a/src/CodeButler/tests/CodeButler.IntegrationTests/IntegrationTests.cs b/src/CodeButler/tests/CodeButler.IntegrationTests/IntegrationTests.cs
index efc8e1d..de361dd 100644
--- a/src/CodeButler/tests/CodeButler.IntegrationTests/IntegrationTests.cs
+++ b/src/CodeButler/tests/CodeButler.IntegrationTests/IntegrationTests.cs
@@ -1,32 +1,55 @@
-using System.Collections.Generic;
+using System;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
using FluentAssertions;
+using Microsoft.CodeAnalysis.CSharp;
using Xunit;
-namespace CodeButler.IntegrationTests
+namespace CodeButler.IntegrationTests;
+
+public class IntegrationTests
{
- public class IntegrationTests
+ private const string _testCasesDir = "TestCases";
+
+ public static TheoryData TestCases()
{
- public static IEnumerable