mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-07 00:14:36 +01:00
Add "enum cloning" source generator
This commit is contained in:
parent
55eb7e41d8
commit
8bb6cdd8d6
14 changed files with 395 additions and 0 deletions
23
Dalamud.sln
23
Dalamud.sln
|
|
@ -75,6 +75,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumina.Excel.Generator", "l
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumina.Excel", "lib\Lumina.Excel\src\Lumina.Excel\Lumina.Excel.csproj", "{88FB719B-EB41-73C5-8D25-C03E0C69904F}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source Generators", "Source Generators", "{50BEC23B-FFFD-427B-A95D-27E1D1958FFF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.EnumGenerator", "generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj", "{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.EnumGenerator.Sample", "generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator.Sample\Dalamud.EnumGenerator.Sample.csproj", "{8CDAEB2D-5022-450A-A97F-181C6270185F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.EnumGenerator.Tests", "generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator.Tests\Dalamud.EnumGenerator.Tests.csproj", "{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -173,6 +181,18 @@ Global
|
|||
{88FB719B-EB41-73C5-8D25-C03E0C69904F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88FB719B-EB41-73C5-8D25-C03E0C69904F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88FB719B-EB41-73C5-8D25-C03E0C69904F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.Build.0 = Release|x64
|
||||
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.Build.0 = Release|x64
|
||||
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -197,6 +217,9 @@ Global
|
|||
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{88FB719B-EB41-73C5-8D25-C03E0C69904F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8} = {50BEC23B-FFFD-427B-A95D-27E1D1958FFF}
|
||||
{8CDAEB2D-5022-450A-A97F-181C6270185F} = {50BEC23B-FFFD-427B-A95D-27E1D1958FFF}
|
||||
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6} = {50BEC23B-FFFD-427B-A95D-27E1D1958FFF}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,15 @@
|
|||
<PackageReference Include="TerraFX.Interop.Windows" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="EnumCloneMap.txt"/>
|
||||
<AdditionalFiles Include="EnumCloneMap.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Interface\ImGuiBackend\Renderers\imgui-frag.hlsl.bytes">
|
||||
<LogicalName>imgui-frag.hlsl.bytes</LogicalName>
|
||||
|
|
|
|||
3
Dalamud/EnumCloneMap.txt
Normal file
3
Dalamud/EnumCloneMap.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Format: Target.Full.TypeName = Source.Full.EnumTypeName
|
||||
# Example: Generate a local enum MyGeneratedEnum in namespace Sample.Gen mapped to SourceEnums.SampleSourceEnum
|
||||
Dalamud.Game.Agent.AgentId = FFXIVClientStructs.FFXIV.Client.UI.Agent.AgentId
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Dalamud.EnumGenerator.Sample</RootNamespace>
|
||||
|
||||
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="EnumCloneMap.txt"/>
|
||||
<AdditionalFiles Include="EnumCloneMap.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Format: Target.Full.TypeName = Source.Full.EnumTypeName
|
||||
# Example: Generate a local enum MyGeneratedEnum in namespace Sample.Gen mapped to SourceEnums.SampleSourceEnum
|
||||
Dalamud.EnumGenerator.Sample.Gen.MyGeneratedEnum = Dalamud.EnumGenerator.Sample.SourceEnums.SampleSourceEnum
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Dalamud.EnumGenerator.Sample.SourceEnums
|
||||
{
|
||||
public enum SampleSourceEnum : long
|
||||
{
|
||||
First = 1,
|
||||
Second = 2,
|
||||
Third = 10000000000L
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<RootNamespace>Dalamud.EnumGenerator.Tests</RootNamespace>
|
||||
|
||||
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.1"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"/>
|
||||
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Dalamud.EnumGenerator.Tests;
|
||||
|
||||
public class EnumCloneMapTests
|
||||
{
|
||||
[Fact]
|
||||
public void ParseMappings_SimpleLines_ParsesCorrectly()
|
||||
{
|
||||
var text = @"# Comment line
|
||||
My.Namespace.Target = Other.Namespace.Source
|
||||
|
||||
Another.Target = Some.Source";
|
||||
|
||||
var results = Dalamud.EnumGenerator.EnumCloneGenerator.ParseMappings(text);
|
||||
|
||||
Assert.Equal(2, results.Length);
|
||||
Assert.Equal("My.Namespace.Target", results[0].TargetFullName);
|
||||
Assert.Equal("Other.Namespace.Source", results[0].SourceFullName);
|
||||
Assert.Equal("Another.Target", results[1].TargetFullName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generator_ProducesFile_WhenSourceResolved()
|
||||
{
|
||||
// We'll create a compilation that contains a source enum type and add an AdditionalText mapping
|
||||
var sourceEnum = @"namespace Foo.Bar { public enum SourceEnum { A = 1, B = 2 } }";
|
||||
|
||||
var mapText = "GeneratedNs.TargetEnum = Foo.Bar.SourceEnum";
|
||||
|
||||
var generator = new EnumCloneGenerator();
|
||||
var driver = CSharpGeneratorDriver.Create(generator)
|
||||
.AddAdditionalTexts(ImmutableArray.Create<AdditionalText>(new Utils.TestAdditionalFile("EnumCloneMap.txt", mapText)));
|
||||
|
||||
var compilation = CSharpCompilation.Create("TestGen", [CSharpSyntaxTree.ParseText(sourceEnum)],
|
||||
[MetadataReference.CreateFromFile(typeof(object).Assembly.Location)]);
|
||||
|
||||
driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics);
|
||||
|
||||
var generated = newCompilation.SyntaxTrees.Select(t => t.FilePath).Where(p => p.EndsWith("TargetEnum.CloneEnum.g.cs")).ToArray();
|
||||
Assert.Single(generated);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using System.Threading;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Dalamud.EnumGenerator.Tests.Utils;
|
||||
|
||||
public class TestAdditionalFile : AdditionalText
|
||||
{
|
||||
private readonly SourceText text;
|
||||
|
||||
public TestAdditionalFile(string path, string text)
|
||||
{
|
||||
Path = path;
|
||||
this.text = SourceText.From(text);
|
||||
}
|
||||
|
||||
public override SourceText GetText(CancellationToken cancellationToken = new()) => this.text;
|
||||
|
||||
public override string Path { get; }
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
; Shipped analyzer releases
|
||||
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
; Unshipped analyzer release
|
||||
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
|
||||
|
||||
### New Rules
|
||||
|
||||
Rule ID | Category | Severity | Notes
|
||||
--------|----------|----------|-------
|
||||
ENUMGEN001 | EnumGenerator | Warning | SourceGeneratorWithAttributes
|
||||
ENUMGEN002 | EnumGenerator | Warning | SourceGeneratorWithAttributes
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
|
||||
<RootNamespace>Dalamud.EnumGenerator</RootNamespace>
|
||||
<PackageId>Dalamud.EnumGenerator</PackageId>
|
||||
|
||||
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
|
||||
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Dalamud.EnumGenerator;
|
||||
|
||||
[Generator]
|
||||
public class EnumCloneGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string NewLine = "\r\n";
|
||||
|
||||
private const string MappingFileName = "EnumCloneMap.txt";
|
||||
|
||||
private static readonly DiagnosticDescriptor MissingSourceDescriptor = new(
|
||||
id: "ENUMGEN001",
|
||||
title: "Source enum not found",
|
||||
messageFormat: "Source enum '{0}' could not be resolved by the compilation",
|
||||
category: "EnumGenerator",
|
||||
defaultSeverity: DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
private static readonly DiagnosticDescriptor DuplicateTargetDescriptor = new(
|
||||
id: "ENUMGEN002",
|
||||
title: "Duplicate target mapping",
|
||||
messageFormat: "Target enum '{0}' is mapped multiple times; generation skipped for this target",
|
||||
category: "EnumGenerator",
|
||||
defaultSeverity: DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// Read mappings from additional files named EnumCloneMap.txt
|
||||
var mappingEntries = context.AdditionalTextsProvider
|
||||
.Where(at => Path.GetFileName(at.Path).Equals(MappingFileName, StringComparison.OrdinalIgnoreCase))
|
||||
.SelectMany((at, _) => ParseMappings(at.GetText()?.ToString() ?? string.Empty));
|
||||
|
||||
// Combine with compilation so we can resolve types
|
||||
var compilationAndMaps = context.CompilationProvider.Combine(mappingEntries.Collect());
|
||||
|
||||
context.RegisterSourceOutput(compilationAndMaps, (spc, pair) =>
|
||||
{
|
||||
var compilation = pair.Left;
|
||||
var maps = pair.Right;
|
||||
|
||||
// Detect duplicate targets first and report diagnostics
|
||||
var duplicateTargets = maps.GroupBy(m => m.TargetFullName, StringComparer.OrdinalIgnoreCase)
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key)
|
||||
.ToImmutableArray();
|
||||
foreach (var dup in duplicateTargets)
|
||||
{
|
||||
var diag = Diagnostic.Create(DuplicateTargetDescriptor, Location.None, dup);
|
||||
spc.ReportDiagnostic(diag);
|
||||
}
|
||||
|
||||
foreach (var (targetFullName, sourceFullName) in maps)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(targetFullName) || string.IsNullOrWhiteSpace(sourceFullName))
|
||||
continue;
|
||||
|
||||
if (duplicateTargets.Contains(targetFullName, StringComparer.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
// Resolve the source enum type by metadata name (namespace.type)
|
||||
var sourceSymbol = compilation.GetTypeByMetadataName(sourceFullName);
|
||||
if (sourceSymbol is null)
|
||||
{
|
||||
// Report diagnostic for missing source type
|
||||
var diag = Diagnostic.Create(MissingSourceDescriptor, Location.None, sourceFullName);
|
||||
spc.ReportDiagnostic(diag);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sourceSymbol.TypeKind != TypeKind.Enum)
|
||||
continue;
|
||||
|
||||
var sourceNamed = sourceSymbol; // GetTypeByMetadataName already returns INamedTypeSymbol
|
||||
|
||||
// Split target into namespace and type name
|
||||
string? targetNamespace = null;
|
||||
var targetName = targetFullName;
|
||||
var lastDot = targetFullName.LastIndexOf('.');
|
||||
if (lastDot >= 0)
|
||||
{
|
||||
targetNamespace = targetFullName.Substring(0, lastDot);
|
||||
targetName = targetFullName.Substring(lastDot + 1);
|
||||
}
|
||||
|
||||
var underlyingType = sourceNamed.EnumUnderlyingType;
|
||||
var underlyingDisplay = underlyingType?.ToDisplayString() ?? "int";
|
||||
|
||||
var fields = sourceNamed.GetMembers()
|
||||
.OfType<IFieldSymbol>()
|
||||
.Where(f => f.IsStatic && f.HasConstantValue)
|
||||
.ToArray();
|
||||
|
||||
var memberLines = fields.Select(f =>
|
||||
{
|
||||
var name = f.Name;
|
||||
var constValue = f.ConstantValue;
|
||||
string literal;
|
||||
|
||||
var st = underlyingType?.SpecialType ?? SpecialType.System_Int32;
|
||||
|
||||
if (constValue is null)
|
||||
{
|
||||
literal = "0";
|
||||
}
|
||||
else if (st == SpecialType.System_UInt64)
|
||||
{
|
||||
literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) + "UL";
|
||||
}
|
||||
else if (st == SpecialType.System_UInt32)
|
||||
{
|
||||
literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) + "U";
|
||||
}
|
||||
else if (st == SpecialType.System_Int64)
|
||||
{
|
||||
literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) + "L";
|
||||
}
|
||||
else
|
||||
{
|
||||
literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) ?? throw new InvalidOperationException("Unable to convert enum constant value to string.");
|
||||
}
|
||||
|
||||
return $" {name} = {literal},";
|
||||
});
|
||||
|
||||
var membersText = string.Join(NewLine, memberLines);
|
||||
|
||||
var nsPrefix = targetNamespace is null ? string.Empty : $"namespace {targetNamespace};" + NewLine + NewLine;
|
||||
|
||||
var code = "// <auto-generated/>" + NewLine + NewLine
|
||||
+ nsPrefix
|
||||
+ $"public enum {targetName} : {underlyingDisplay}" + NewLine
|
||||
+ "{" + NewLine
|
||||
+ membersText + NewLine
|
||||
+ "}" + NewLine;
|
||||
|
||||
var hintName = $"{targetName}.CloneEnum.g.cs";
|
||||
spc.AddSource(hintName, SourceText.From(code, Encoding.UTF8));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal static ImmutableArray<(string TargetFullName, string SourceFullName)> ParseMappings(string text)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<(string, string)>();
|
||||
using var reader = new StringReader(text);
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
// Remove comments starting with #
|
||||
var commentIndex = line.IndexOf('#');
|
||||
var content = commentIndex >= 0 ? line.Substring(0, commentIndex) : line;
|
||||
content = content.Trim();
|
||||
if (string.IsNullOrEmpty(content))
|
||||
continue;
|
||||
|
||||
// Expected format: Target.Full.Name = Source.Full.Name
|
||||
var idx = content.IndexOf('=');
|
||||
if (idx <= 0)
|
||||
continue;
|
||||
|
||||
var left = content.Substring(0, idx).Trim();
|
||||
var right = content.Substring(idx + 1).Trim();
|
||||
if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right))
|
||||
continue;
|
||||
|
||||
builder.Add((left, right));
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Dalamud.EnumGenerator.Tests")]
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue