Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 42 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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`

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -160,15 +171,16 @@ Compile Settings:
"Platform": "Oxide",
"Merge Settings": {
"Plugin Name": "MyPluginName",
"Plugin Base Class": "CovalencePlugin",
"Creator Mode": "Plugin",
"Namespace Override": "",
"Plugin Input Paths": [
"./"
],
"Plugin Output Paths": [
"./build"
],
"Reference Definitions": [],
"Requires Definitions": [],
"Define Definitions": [
"DEBUG"
],
Expand All @@ -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"
Expand Down
5 changes: 0 additions & 5 deletions src/PluginMerge/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ public static class Definitions
/// Files with this comment will be ignored
/// </summary>
public const string ExcludeFile = "//Define:ExcludeFile";

/// <summary>
/// Files with this comment will be added as extension methods
/// </summary>
public const string ExtensionFile = "//Define:ExtensionMethods";

/// <summary>
/// Files with this comment will be ordered based on the tag
Expand Down
122 changes: 82 additions & 40 deletions src/PluginMerge/Creator/FileCreator.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace PluginMerge.Creator;

/// <summary>
Expand Down Expand Up @@ -70,22 +72,27 @@ 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();
if (IsPluginMode || IsFrameworkMode) WriteDataFiles();
EndPluginClass();
if (IsMergeFrameworkMode) WriteDataFiles();
WriteFrameworks();
EndNamespace();
WriteExtensionMethods();
if (hasExtensionMethods)
{
EndNamespace();
WriteExtensionMethods();
}
WriteErrorPreprocessorMessages();
return true;
}
Expand Down Expand Up @@ -188,44 +195,53 @@ private void WriteDefines()
/// </summary>
private void WriteUsings()
{
List<string> 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();
}
}

/// <summary>
/// writes usings to the code writer
/// </summary>
private void WriteNamespaceUsings()
{
WriteUsings(_files.SelectMany(f => f.UsingStatements.Select(u => new UsingStatementData(u))).Where(u => u.IsNamespace), []);
}

private void WriteUsings(IEnumerable<UsingStatementData> 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));
}

/// <summary>
/// Writes namespace to the code writer
/// </summary>
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();
}
}

/// <summary>
Expand All @@ -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)
Expand Down Expand Up @@ -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();
Expand All @@ -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
}
}
Loading