diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1b37fdd..3653ac9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '10.0.x'
- name: Build
run: dotnet build --configuration Release
working-directory: src
\ No newline at end of file
diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml
index 2148aa6..30b39a4 100644
--- a/.github/workflows/pre-release.yml
+++ b/.github/workflows/pre-release.yml
@@ -16,7 +16,7 @@ jobs:
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '10.0.x'
- name: Build
run: dotnet build --configuration Release /p:Version=${VERSION}
working-directory: src
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6620086..303aec4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,7 +20,7 @@ jobs:
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '10.0.x'
- name: Build
run: dotnet build --configuration Release /p:Version=${VERSION}
working-directory: src
diff --git a/README.md b/README.md
index 860d5aa..25d7505 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ Plugin Merge is a .net 6+ CLI tool that allows merging multiple .cs files into a
[Discord Chat](https://github.com/dassjosh/Plugin.DiscordChat)
[Discord Core](https://github.com/dassjosh/Plugin.DiscordCore)
[Discord Players](https://github.com/dassjosh/Plugin.DiscordPlayers)
+[Discord Roles](https://github.com/dassjosh/Plugin.DiscordRoles)
## Installation
`dotnet tool install --global MJSU.Plugin.Merge` from the shell/command line.
@@ -64,9 +65,9 @@ Place your config file a directory near your plugin .cs files.
Update the config paths to point to the input and output paths you would like to use.
The configuration supports relative pathing and all paths use the configuration files directory as it's staring point.
-Once your configuration file is setup it's time to merge the files together.
+Once your configuration file is setup it's time to merge the files.
You can run the merge by typing `plugin.merge -m -p ./merge.yml`.
-This will merge all the .cs files together and create a final file in the output paths specified.
+This will merge all the .cs files and create a final file in the output paths specified.
You can also enable compilation to compile your plugin to check for any issues before loading it onto your server.
To enable compilation add the `-c` argument while merging Ex: `plugin.merge -m -c -p ./merge.yml`
@@ -98,37 +99,39 @@ There are 3 types of merge options when using Plugin Merge.
### YAML Configuration File
```yaml
-# What platform to write the code file for (Oxide, uMod)
+# What platform to write the code file for (Oxide)
Platform: Oxide
Merge Settings:
-# Outputted plugin name
+ # Outputted plugin name
Plugin Name: MyPluginName
- # Outputted plugin base class
- Plugin Base Class: CovalencePlugin
# Which type of file to output (Plugin, Framework, or MergeFramework)
Creator Mode: Plugin
+ # Overrides the default namespace
+ Namespace Override: ''
# Paths to use when reading in source code relative to the merge config
Plugin Input Paths:
- - ./
+ - ./
# Paths to use when writing the plugin file relative to the merge config
Plugin Output Paths:
- - ./build
+ - ./build
# Oxide //References: definitions
Reference Definitions: []
+ # Oxide //Requires: definitions
+ Requires Definitions: []
# #define definitions
Define Definitions:
- - DEBUG
+ - DEBUG
# Paths to be ignored when searching for source code relative to merge config
Ignore Paths:
- - ./IgnoreThisPath
+ - ./IgnoreThisPath
# Files to be ignored when searching for source code relative to merge config
Ignore Files:
- - ./IgnoreThisFile.cs
+ - ./IgnoreThisFile.cs
# Namespaces to ignore when processing output file
Ignore Namespaces:
- - IgnoreThisNameSpace
+ - IgnoreThisNameSpace
Code Style:
- # Character to use for code indents
+ # Character to use for code indents
Indent Character: ' '
# The amount of characters to use when indenting once
Indent Char Amount: 4
@@ -140,18 +143,26 @@ Merge Settings:
Write The Relative File Path In Region: true
# Adds the code file path in a region
Keep Code Comments: true
+Preprocessor Directive Settings:
+ # Preprocessor Directives that are required to build the plugin
+ Preprocessor Directives:
+ - # The Directive That Is Required By The Plugin
+ Directive: OXIDE
+ # The Compiler Error Message Show When The Directive Is Missing
+ Error Message: This plugin requires OXIDE
+ # If This Directive Is Enabled
+ Enabled: false
Compile Settings:
- AssemblyPaths:
- - ./Assemblies
+ Assembly Paths:
+ - ./Assemblies
# Ignores the following paths relative to the merge config
Ignore Paths:
- - ./Assemblies/x86
- - ./Assemblies/x64
+ - ./Assemblies/x86
+ - ./Assemblies/x64
# Ignores the following files relative to the merge config
Ignore Files:
- - ./Assemblies/Newtonsoft.Json.dll
+ - ./Assemblies/Newtonsoft.Json.dll
Compile Log Level (Hidden, Info, Warning, Error): Error
-
```
### JSON Configuration File
@@ -160,8 +171,8 @@ Compile Settings:
"Platform": "Oxide",
"Merge Settings": {
"Plugin Name": "MyPluginName",
- "Plugin Base Class": "CovalencePlugin",
"Creator Mode": "Plugin",
+ "Namespace Override": "",
"Plugin Input Paths": [
"./"
],
@@ -169,6 +180,7 @@ Compile Settings:
"./build"
],
"Reference Definitions": [],
+ "Requires Definitions": [],
"Define Definitions": [
"DEBUG"
],
@@ -187,9 +199,18 @@ Compile Settings:
"Indent Multiplier": 1,
"New Line String": "\r\n",
"Write The Relative File Path In Region": true,
- "Keep Comments": true
+ "Keep Code Comments": true
}
},
+ "Preprocessor Directive Settings": {
+ "Preprocessor Directives": [
+ {
+ "Directive": "OXIDE",
+ "Error Message": "This plugin requires OXIDE",
+ "Enabled": false
+ }
+ ]
+ },
"Compile Settings": {
"Assembly Paths": [
"./Assemblies"
diff --git a/src/PluginMerge/Constants.cs b/src/PluginMerge/Constants.cs
index 10bb5a1..e19d10d 100644
--- a/src/PluginMerge/Constants.cs
+++ b/src/PluginMerge/Constants.cs
@@ -21,11 +21,6 @@ public static class Definitions
/// Files with this comment will be ignored
///
public const string ExcludeFile = "//Define:ExcludeFile";
-
- ///
- /// Files with this comment will be added as extension methods
- ///
- public const string ExtensionFile = "//Define:ExtensionMethods";
///
/// Files with this comment will be ordered based on the tag
diff --git a/src/PluginMerge/Creator/FileCreator.cs b/src/PluginMerge/Creator/FileCreator.cs
index 3a6fc1b..4081fe5 100644
--- a/src/PluginMerge/Creator/FileCreator.cs
+++ b/src/PluginMerge/Creator/FileCreator.cs
@@ -1,3 +1,5 @@
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
namespace PluginMerge.Creator;
///
@@ -70,13 +72,15 @@ public bool Create()
FilterFiles(_plugin.PluginData);
_writer = new CodeWriter(_plugin.PluginData, _settings.Merge);
+ bool hasExtensionMethods = _extensionTypes.Count != 0;
WriteRequires();
WriteReferences();
WriteRequiredPreprocessorDirectives();
WriteDefines();
WriteUsings();
- WriteNamespace();
+ WriteNamespace(!hasExtensionMethods);
+ WriteNamespaceUsings();
if (IsMergeFrameworkMode) _writer.WriteFramework();
StartPluginClass();
WritePluginFiles();
@@ -84,8 +88,11 @@ public bool Create()
EndPluginClass();
if (IsMergeFrameworkMode) WriteDataFiles();
WriteFrameworks();
- EndNamespace();
- WriteExtensionMethods();
+ if (hasExtensionMethods)
+ {
+ EndNamespace();
+ WriteExtensionMethods();
+ }
WriteErrorPreprocessorMessages();
return true;
}
@@ -188,44 +195,53 @@ private void WriteDefines()
///
private void WriteUsings()
{
- List extensionNameSpaces = _files
- .SelectMany(f => f.FileTypes
+ string[] extensionNameSpaces = _files.SelectMany(f => f.FileTypes
.Where(f => f.IsExtensionMethods())
.Select(f => f.TypeNamespace))
- .ToList();
-
- var uniqueUsings = _files
- .SelectMany(f => f.UsingStatements.Select(u => new
- {
- File = f,
- Using = u,
- UsingText = u.ToString(),
- UsingName = u.Name.ToString()
- }))
- .DistinctBy(u => u.UsingText)
- .Where(u => !_settings.Merge.IgnoreNameSpaces.Any(u.UsingName.StartsWith) && !extensionNameSpaces.Contains(u.UsingText))
- .OrderBy(u => u.UsingText)
- .ToArray();
+ .ToArray();
- _writer.WriteUsings(uniqueUsings.Where(u => u.Using.Alias == null && u.Using.StaticKeyword == default).Select(u => u.UsingText));
- _writer.WriteUsings(uniqueUsings.Where(u => u.Using.StaticKeyword != default).Select(u => u.UsingText));
- _writer.WriteUsings(uniqueUsings.Where(u => u.Using.Alias is not null).Select(u => u.UsingText));
+ WriteUsings(_files.SelectMany(f => f.UsingStatements.Select(u => new UsingStatementData(u))).Where(u => !u.IsNamespace), extensionNameSpaces);
if (_extensionTypes.Count != 0)
{
_writer.WriteUsing(GetExtensionNamespace());
+ _writer.WriteLine();
}
}
+
+ ///
+ /// writes usings to the code writer
+ ///
+ private void WriteNamespaceUsings()
+ {
+ WriteUsings(_files.SelectMany(f => f.UsingStatements.Select(u => new UsingStatementData(u))).Where(u => u.IsNamespace), []);
+ }
+
+ private void WriteUsings(IEnumerable usings, string[] extensionNameSpaces)
+ {
+ UsingStatementData[] uniqueUsings = usings
+ .DistinctBy(u => u.UsingText)
+ .Where(u => !_settings.Merge.IgnoreNameSpaces.Any(u.UsingName.StartsWith) && !extensionNameSpaces.Contains(u.UsingText))
+ .OrderBy(u => u.UsingText)
+ .ToArray();
+
+ _writer.WriteUsings(uniqueUsings.Where(u => u.IsDefault).Select(u => u.UsingText));
+ _writer.WriteUsings(uniqueUsings.Where(u => u.IsStatic).Select(u => u.UsingText));
+ _writer.WriteUsings(uniqueUsings.Where(u => u.IsAlias).Select(u => u.UsingText));
+ }
///
/// Writes namespace to the code writer
///
- private void WriteNamespace()
+ private void WriteNamespace(bool fileScoped)
{
_writer.WriteComment($"{_settings.Merge.PluginName} created with PluginMerge v({typeof(Program).Assembly.GetName().Version}) by MJSU @ https://github.com/dassjosh/Plugin.Merge");
- _writer.WriteNamespace(_settings.PlatformSettings.Namespace);
+ _writer.WriteNamespace(_settings.PlatformSettings.Namespace, fileScoped);
_writer.WriteLine();
- _writer.WriteStartBracket();
+ if (!fileScoped)
+ {
+ _writer.WriteStartBracket();
+ }
}
///
@@ -234,7 +250,7 @@ private void WriteNamespace()
private void WriteExtensionNamespace()
{
_writer.WriteLine();
- _writer.WriteNamespace(GetExtensionNamespace());
+ _writer.WriteNamespace(GetExtensionNamespace(), false);
_writer.WriteLine();
_writer.WriteStartBracket();
if (_settings.Merge.CreatorMode != CreatorMode.MergeFramework)
@@ -371,24 +387,10 @@ private void WriteFrameworks()
private void WriteExtensionMethods()
{
- if (_extensionTypes.Count == 0)
- {
- return;
- }
-
- bool isFramework = IsFrameworkMode || IsMergeFrameworkMode;
-
WriteExtensionNamespace();
foreach (IFileType type in _extensionTypes)
{
_logger.LogDebug("Writing extension type: {Path}", type.TypeName);
-
- if (isFramework)
- {
- _writer.WriteLine();
- _writer.WriteDefinition(Constants.Definitions.ExtensionFile);
- }
-
type.Write(_writer);
}
EndNamespace();
@@ -407,4 +409,44 @@ private string GetExtensionNamespace()
{
return $"{_settings.PlatformSettings.Namespace}.{_settings.Merge.PluginName}Extensions";
}
+
+
+ private sealed class UsingStatementData
+ {
+ public readonly string UsingText;
+ public readonly string UsingName;
+ private readonly UsingType _type;
+
+ public bool IsDefault => !IsStatic && !IsAlias;
+ public bool IsStatic => _type.HasFlag(UsingType.Static);
+ public bool IsAlias => _type.HasFlag(UsingType.Alias);
+ public bool IsNamespace => _type.HasFlag(UsingType.Namespace);
+
+ public UsingStatementData(UsingDirectiveSyntax @using)
+ {
+ UsingText = @using.ToString();
+ UsingName = @using.Name.ToString();
+ if (@using.StaticKeyword != default)
+ {
+ _type |= UsingType.Static;
+ }
+ if (@using.Alias is not null)
+ {
+ _type |= UsingType.Alias;
+ }
+ if(@using.Parent is BaseNamespaceDeclarationSyntax)
+ {
+ _type |= UsingType.Namespace;
+ }
+ }
+ }
+
+ [Flags]
+ private enum UsingType
+ {
+ Default = 0,
+ Static = 1 << 0,
+ Alias = 1 << 1,
+ Namespace = 1 << 2
+ }
}
\ No newline at end of file
diff --git a/src/PluginMerge/Merge/FileHandler.cs b/src/PluginMerge/Merge/FileHandler.cs
index 74ec5d9..e15af96 100644
--- a/src/PluginMerge/Merge/FileHandler.cs
+++ b/src/PluginMerge/Merge/FileHandler.cs
@@ -97,7 +97,7 @@ public async Task ProcessFile(PlatformSettings settings, CSharpParseOptions opti
return;
}
- await Task.WhenAll(ProcessUsings(settings, root), ProcessNamespace(root)).ConfigureAwait(false);
+ await Task.WhenAll(ProcessUsings(settings, root), ProcessNamespace(settings, root)).ConfigureAwait(false);
}
private Task ProcessComments(CompilationUnitSyntax root)
@@ -173,7 +173,13 @@ private void ProcessFrameworkComments(string comment)
private Task ProcessUsings(PlatformSettings settings, CompilationUnitSyntax root)
{
_logger.LogDebug("Start processing usings file at path: {Path}", RegionName);
- foreach (UsingDirectiveSyntax @using in root.Usings)
+ AddUsings(settings, root.Usings);
+ return Task.CompletedTask;
+ }
+
+ private void AddUsings(PlatformSettings settings, SyntaxList usings)
+ {
+ foreach (UsingDirectiveSyntax @using in usings)
{
string name = @using.Name.ToString();
if (!name.Equals(settings.Namespace))
@@ -181,25 +187,28 @@ private Task ProcessUsings(PlatformSettings settings, CompilationUnitSyntax root
UsingStatements.Add(@using);
}
}
-
- return Task.CompletedTask;
}
-
- private Task ProcessNamespace(CompilationUnitSyntax root)
+
+ private Task ProcessNamespace(PlatformSettings settings, CompilationUnitSyntax root)
{
_logger.LogDebug("Start processing namespace file at path: {Path}", RegionName);
foreach (BaseNamespaceDeclarationSyntax @namespace in root.ChildNodes().OfType())
{
+ AddUsings(settings, @namespace.Usings);
+
foreach (BaseTypeDeclarationSyntax node in @namespace.ChildNodes().OfType())
{
FileSettings typeSettings = Settings;
- foreach (SyntaxTrivia trivia in node.DescendantTrivia())
+ if (node is ClassDeclarationSyntax @class && @class.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
{
- if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
+ foreach (MethodDeclarationSyntax method in @class.ChildNodes().OfType())
{
- if (trivia.ToString() == Constants.Definitions.ExtensionFile)
+ if (method.ParameterList.Parameters.Count != 0)
{
- typeSettings |= FileSettings.Extension;
+ if (method.ParameterList.Parameters[0].Modifiers.Any(m => m.IsKind(SyntaxKind.ThisKeyword)))
+ {
+ typeSettings |= FileSettings.Extension;
+ }
}
}
}
diff --git a/src/PluginMerge/PluginMerge.csproj b/src/PluginMerge/PluginMerge.csproj
index d93b6c2..030fb0a 100644
--- a/src/PluginMerge/PluginMerge.csproj
+++ b/src/PluginMerge/PluginMerge.csproj
@@ -7,7 +7,7 @@
./bin/nupkg
$(Version)
$(Version)
- 10
+ 14
MJSU
enable
$(Version)
diff --git a/src/PluginMerge/Writer/CodeWriter.cs b/src/PluginMerge/Writer/CodeWriter.cs
index 3320c3e..0983b34 100644
--- a/src/PluginMerge/Writer/CodeWriter.cs
+++ b/src/PluginMerge/Writer/CodeWriter.cs
@@ -106,6 +106,7 @@ public void WriteUsings(IEnumerable usings)
foreach (string @using in usings)
{
didWrite = true;
+ WriteIndent();
_writer.AppendLine(@using);
}
@@ -137,10 +138,15 @@ public void WriteUsingAlias(string type, string typeNamespace)
/// Writes namespace to the code
///
///
- public void WriteNamespace(string @namespace)
+ public void WriteNamespace(string @namespace, bool isFileScoped)
{
_writer.Append("namespace ");
_writer.Append(@namespace);
+ if (isFileScoped)
+ {
+ _writer.Append(';');
+ _writer.AppendLine();
+ }
}
///