-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Foundation for string builder approach
- Loading branch information
1 parent
277412e
commit f8943f6
Showing
17 changed files
with
358 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# CodeOfChaos.Ansi |
148 changes: 148 additions & 0 deletions
148
src/CodeOfChaos.Ansi.Generators/AnsiStringBuilderGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Imports | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
using CodeOfChaos.Ansi.Generators.Xml; | ||
using CodeOfChaos.GeneratorTools; | ||
using Microsoft.CodeAnalysis; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
using System.Xml.Serialization; | ||
|
||
namespace CodeOfChaos.Ansi.Generators; | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Code | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
[Generator] | ||
public class AnsiStringBuilderGenerator : IIncrementalGenerator { | ||
private static Regex FindFirstColorName { get; } = new(@"ASB\.[^\\\/]*\.xml"); | ||
|
||
// ----------------------------------------------------------------------------------------------------------------- | ||
// Methods | ||
// ----------------------------------------------------------------------------------------------------------------- | ||
public void Initialize(IncrementalGeneratorInitializationContext context) { | ||
IncrementalValueProvider<ImmutableArray<AdditionalText>> provider = context.AdditionalTextsProvider | ||
.Where(IsColorFile) | ||
.Collect(); | ||
|
||
context.RegisterSourceOutput(provider, GenerateCode); | ||
} | ||
|
||
private static bool IsColorFile(AdditionalText f) => FindFirstColorName.IsMatch(f.Path); | ||
|
||
private static void GenerateCode(SourceProductionContext context, ImmutableArray<AdditionalText> files) { | ||
IEnumerable<ColorEntry> colors = files.SelectMany(file => ParseColorFile(context, file)).ToArray(); | ||
var builder = new GeneratorStringBuilder(); | ||
|
||
#region Fore & Background | ||
foreach (string section in new[] {"Foreground", "Background"}) { | ||
context.AddSource($"Ansi{section}Builder.g.cs", builder | ||
.AppendUsings("System") | ||
.AppendAutoGenerated() | ||
.AppendNamespace("CodeOfChaos.Ansi") | ||
.AppendLine($"public partial class Ansi{section}Builder {{") | ||
.ForEach(colors, (stringBuilder, entry) => stringBuilder | ||
.AppendBodyIndented($$""" | ||
#region {{entry.Name}} | ||
private static CodeOfChaos.Ansi.ByteVector3 _{{entry.Name}} = new({{entry.Colors}}); | ||
public string {{entry.Name}}(string text) => $"{CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})}{text}{CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes}"; | ||
public Ansi{{section}}Builder Append{{entry.Name}}(string text) => BuilderAction(() => { | ||
Builder | ||
.Append(CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})) | ||
.Append(text) | ||
.Append(CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes); | ||
}); | ||
public Ansi{{section}}Builder Append{{entry.Name}}(Func<string> action) => BuilderAction(() => { | ||
Builder | ||
.Append(CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})) | ||
.Append(action()) | ||
.Append(CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes); | ||
}); | ||
public Ansi{{section}}Builder Append{{entry.Name}}(Action<Ansi{{section}}Builder> action) => BuilderAction(() => { | ||
Builder.Append(CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})); | ||
action(this); | ||
Builder.Append(CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes); | ||
}); | ||
public Ansi{{section}}Builder Append{{entry.Name}}Line(string text) => BuilderAction(() => { | ||
Builder | ||
.Append(CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})) | ||
.Append(text) | ||
.AppendLine(CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes); | ||
}); | ||
public Ansi{{section}}Builder Append{{entry.Name}}Line(Func<string> action) => BuilderAction(() => { | ||
Builder | ||
.Append(CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})) | ||
.Append(action()) | ||
.AppendLine(CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes); | ||
}); | ||
public Ansi{{section}}Builder Append{{entry.Name}}Line(Action<Ansi{{section}}Builder> action) => BuilderAction(() => { | ||
Builder.Append(CodeOfChaos.Ansi.AnsiCodes.Rgb{{section}}Color(_{{entry.Name}})); | ||
action(this); | ||
Builder.AppendLine(CodeOfChaos.Ansi.AnsiCodes.ResetGraphicsModes); | ||
}); | ||
#endregion | ||
""") | ||
.AppendLine() | ||
) | ||
.AppendLine("}") | ||
.ToStringAndClear() | ||
); | ||
} | ||
#endregion | ||
} | ||
|
||
private static ColorEntry[] ParseColorFile(SourceProductionContext context, AdditionalText file) { | ||
try { | ||
string? fileContent = file.GetText(context.CancellationToken)?.ToString(); | ||
if (fileContent is null || string.IsNullOrWhiteSpace(fileContent)) return []; | ||
|
||
// Use XmlSerializer to deserialize XML | ||
var serializer = new XmlSerializer(typeof(ColorEntryContainer)); | ||
using var reader = new StringReader(fileContent); | ||
// Deserialize the XML into the container object | ||
var container = (ColorEntryContainer?)serializer.Deserialize(reader); | ||
|
||
// Map the Color attribute (comma-separated values) to the ColorCode int array | ||
return container?.Entries | ||
.Select(entry => entry.ToColorEntry()) | ||
.ToArray() ?? []; | ||
} | ||
catch (InvalidOperationException ex) { | ||
// Report a diagnostic if XML parsing fails | ||
context.ReportDiagnostic(Diagnostic.Create( | ||
new DiagnosticDescriptor( | ||
"ASB01", | ||
"Invalid XML Format", | ||
$"Failed to parse the XML file: {file.Path}. Error: {ex.Message}", | ||
"CodeGeneration", | ||
DiagnosticSeverity.Warning, | ||
true), | ||
Location.None)); | ||
return []; | ||
} | ||
catch (FormatException ex) { | ||
// Handle errors when parsing color values | ||
context.ReportDiagnostic(Diagnostic.Create( | ||
new DiagnosticDescriptor( | ||
"ASB02", | ||
"Invalid Color Formatting", | ||
$"Error while parsing RGB values in file: {file.Path}. Error: {ex.Message}", | ||
"CodeGeneration", | ||
DiagnosticSeverity.Warning, | ||
true), | ||
Location.None)); | ||
return []; | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
src/CodeOfChaos.Ansi.Generators/Properties/launchSettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/launchsettings.json", | ||
"profiles": { | ||
"DebugRoslynSourceGenerator": { | ||
"commandName": "DebugRoslynComponent", | ||
"targetProject": "../CodeOfChaos.Ansi/CodeOfChaos.Ansi.csproj" | ||
} | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/CodeOfChaos.Ansi.Generators/Xml/ColorEntryContainer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Imports | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
using System.Xml.Serialization; | ||
|
||
namespace CodeOfChaos.Ansi.Generators.Xml; | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Code | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
[XmlRoot("ColorEntries")] | ||
public class ColorEntryContainer { | ||
[XmlElement("ColorEntry")] | ||
public List<XmlColorEntry> Entries { get; set; } = [];// Default to an empty list | ||
} | ||
|
||
public class XmlColorEntry { | ||
[XmlAttribute("Name")] | ||
public string Name { get; set; } = string.Empty;// Name attribute for the color | ||
|
||
[XmlAttribute("Color")] | ||
public string Color { get; set; } = string.Empty;// RGB values as a comma-separated string | ||
|
||
private readonly Regex _regexCommaSeparated = new(@"^(\d+)[,.;:](\d+)[,.;:](\d+)$", RegexOptions.Compiled); | ||
private readonly Regex _regexHexFormat = new(@"^#?([A-Fa-f0-9]{6})$", RegexOptions.Compiled); | ||
|
||
public ColorEntry ToColorEntry() { | ||
if (_regexCommaSeparated.Match(Color) is {Success : true } match) return new ColorEntry { | ||
Name = Name, | ||
Codes = [ | ||
int.Parse(match.Groups[1].Value), | ||
int.Parse(match.Groups[2].Value), | ||
int.Parse(match.Groups[3].Value) | ||
] | ||
}; | ||
|
||
if (_regexHexFormat.IsMatch(Color)) { | ||
string hex = Color.TrimStart('#'); | ||
return new ColorEntry { | ||
Name = Name, | ||
Codes = Enumerable.Range(0, 3) | ||
.Select(i => Convert.ToInt32(hex.Substring(i * 2, 2), 16)) | ||
.ToArray() | ||
}; | ||
} | ||
|
||
throw new FormatException("Invalid Color format."); | ||
} | ||
} | ||
|
||
public class ColorEntry { | ||
public string Name { get; set; } = string.Empty;// Mapped Name | ||
|
||
public int[] Codes { get; set; } = [];// Mapped and split RGB values | ||
|
||
public string Colors => string.Join(",", Codes); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<ColorEntries> | ||
<ColorEntry Name="Red" Color="FF0000"/> | ||
<ColorEntry Name="Green" Color="0,255,0"/> | ||
<ColorEntry Name="Blue" Color="0,0,255"/> | ||
<ColorEntry Name="Yellow" Color="255,255,0"/> | ||
</ColorEntries> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Imports | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
namespace CodeOfChaos.Ansi; | ||
|
||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Code | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
public partial class AnsiBackgroundBuilder : AnsiStringBuilder { | ||
private AnsiBackgroundBuilder BuilderAction(Action action) { | ||
action(); | ||
return this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Imports | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
namespace CodeOfChaos.Ansi; | ||
|
||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Code | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
public partial class AnsiForegroundBuilder : AnsiStringBuilder { | ||
private AnsiForegroundBuilder BuilderAction(Action action) { | ||
action(); | ||
return this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Imports | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
using System.Text; | ||
|
||
namespace CodeOfChaos.Ansi; | ||
|
||
// --------------------------------------------------------------------------------------------------------------------- | ||
// Code | ||
// --------------------------------------------------------------------------------------------------------------------- | ||
public partial class AnsiStringBuilder { | ||
internal StringBuilder Builder { get; init; } = new(); | ||
public int Length => Builder.Length; | ||
|
||
public AnsiForegroundBuilder Foreground => new() { Builder = Builder }; | ||
public AnsiForegroundBuilder Fore => Foreground; | ||
|
||
public AnsiBackgroundBuilder Background => new() { Builder = Builder }; | ||
public AnsiBackgroundBuilder Back => Background; | ||
|
||
// ----------------------------------------------------------------------------------------------------------------- | ||
// Methods | ||
// ----------------------------------------------------------------------------------------------------------------- | ||
private AnsiStringBuilder BuilderAction(Action action) { | ||
action(); | ||
return this; | ||
} | ||
|
||
public AnsiStringBuilder WithForeground(Action<AnsiForegroundBuilder> action) => BuilderAction(() => action(Foreground)); | ||
public AnsiStringBuilder WithFore(Action<AnsiForegroundBuilder> action) => BuilderAction(() => action(Foreground)); | ||
|
||
public AnsiStringBuilder WithBackground(Action<AnsiForegroundBuilder> action) => BuilderAction(() => action(Foreground)); | ||
public AnsiStringBuilder WithBack(Action<AnsiForegroundBuilder> action) => BuilderAction(() => action(Foreground)); | ||
|
||
public AnsiStringBuilder Append(string value) => BuilderAction(() => Builder.Append(value)); | ||
public AnsiStringBuilder AppendLine(string value) => BuilderAction(() => Builder.AppendLine(value)); | ||
|
||
public string ToStringAndClear() { | ||
string result = Builder.ToString(); | ||
Builder.Clear(); | ||
return result; | ||
} | ||
|
||
public override string ToString() => Builder.ToString(); | ||
public void Clear() => Builder.Clear(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.