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
21 changes: 21 additions & 0 deletions Figgle.Fonts.Generator/Figgle.Fonts.Generator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsRoslynComponent>true</IsRoslynComponent>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
<PackageReference Include="IsExternalInit" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Figgle\Figgle.csproj" />
</ItemGroup>
</Project>
167 changes: 167 additions & 0 deletions Figgle.Fonts.Generator/FiggleFontGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright Drew Noakes. Licensed under the Apache-2.0 license. See the LICENSE file for more details.

using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Security;
using System.Text;
using Microsoft.CodeAnalysis;

namespace Figgle.Fonts.Generator;

/// <summary>
/// This is an internal source generator that populates the FiggleFonts class with properties for each bundled font.
/// It is not intended for public use, but rather to automate the generation of the FiggleFonts class
/// </summary>
[Generator(LanguageNames.CSharp)]
internal sealed class FiggleFontGenerator : IIncrementalGenerator
{
private static readonly string[] _newLineCharacters = ["\r\n", "\n"];

private static readonly DiagnosticDescriptor _errorParsingFontFileDiagnostic = new(
"FIGGLE_IMPL001",
"Font Parsing Error",
"Failed to parse font '{0}'. Ensure it is a valid FIGlet font file.",
"FiggleFonts",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

private const string Header =
"""
// Copyright Drew Noakes. Licensed under the Apache-2.0 license. See the LICENSE file for more details.

// <auto-generated>
// This code was generated by Figgle.Fonts.Generator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>

""";

public void Initialize(IncrementalGeneratorInitializationContext context)
{
var fontMemberNamesProvider = context.AdditionalTextsProvider
.Where(file => Path.GetFileName(file.Path).Equals("Aliases.csv", StringComparison.OrdinalIgnoreCase))
.Select(static (file, cancellationToken) =>
{
var csvFileContent = file.GetText(cancellationToken)?.ToString();
if (csvFileContent is null)
{
return [];
Comment thread
jonathanou marked this conversation as resolved.
}

var fontInfos = ImmutableArray.CreateBuilder<FontInfo>();
var entries = csvFileContent.Split(_newLineCharacters, StringSplitOptions.RemoveEmptyEntries);
foreach (var entry in entries)
{
var components = entry.Split(',');
if (components.Length == 2)
{
var fontName = components[0].Trim();
var memberName = components[1].Trim();
fontInfos.Add(new FontInfo(fontName, memberName));
}
}

return fontInfos.ToImmutable();
});

var parsedFontsProvider = context.AdditionalTextsProvider
.Where(file => file.Path.EndsWith(".flf", StringComparison.OrdinalIgnoreCase))
.Select(static (file, cancellationToken) =>
{
var fontContent = file.GetText(cancellationToken)?.ToString();
return new ParsedFont(
Path.GetFileNameWithoutExtension(file.Path),
fontContent is null ? null : FiggleFontParser.ParseString(fontContent));
})
.Collect();

context.RegisterSourceOutput(fontMemberNamesProvider.Combine(parsedFontsProvider), (context, pair) =>
{
var fontInfos = pair.Left;
var parsedFonts = pair.Right.ToImmutableDictionary(
keySelector: f => f.Name,
elementSelector: f => f.Font);

foreach (var kvp in parsedFonts)
{
if (kvp.Value is null)
{
context.ReportDiagnostic(Diagnostic.Create(
_errorParsingFontFileDiagnostic,
Location.None,
kvp.Key));
}
}

var source = $$"""
{{Header}}
namespace Figgle.Fonts;

partial class FiggleFonts
{{{RenderFiggleFontProperties(fontInfos, parsedFonts)}}
}
""";

// we expect only a single Alias.csv file, so we don't need to worry about making
// the source file name unique.
context.AddSource("FiggleFonts.g.cs", source);

static string RenderFiggleFontProperties(
ImmutableArray<FontInfo> fontInfos,
ImmutableDictionary<string, FiggleFont?> parsedFonts)
{
var builder = new StringBuilder(capacity: 4096);
var indentation = new string(' ', 4);
foreach (var fontInfo in fontInfos)
{
builder.Append($$"""


{{indentation}}/// <summary>
{{indentation}}/// Obtains the <see cref="Figgle.FiggleFont" /> for the font name "{{fontInfo.FontName}}".
{{indentation}}/// <example>
{{indentation}}/// <code>
{{indentation}}{{RenderSampleText(fontInfo.FontName, parsedFonts[fontInfo.FontName], indentation)}}
{{indentation}}/// </code>
{{indentation}}/// </example>
{{indentation}}/// </summary>
{{indentation}}public static FiggleFont {{fontInfo.MemberName}} => GetByName("{{fontInfo.FontName}}");
""");
}

return builder.ToString();
}

static string RenderSampleText(string fontName, FiggleFont? font, string indentation)
{
if (font is null)
{
return $"Failed to parse {fontName} into a {nameof(FiggleFont)}";
}

var renderedText = font.Render(fontName);

return string.Join(
$"\r\n{indentation}",
renderedText
.Split(_newLineCharacters, StringSplitOptions.None)
.Select(line => $"/// {EscapeXmlSpecialCharacters(line)}"));
}

static string EscapeXmlSpecialCharacters(string text)
{
// SecurityElement conveniently has Escape that escapes XML special
// characters, so we can just reuse it.
return SecurityElement.Escape(text);
Comment thread
drewnoakes marked this conversation as resolved.
}
});
}

private sealed record FontInfo(string FontName, string MemberName);

private sealed record ParsedFont(string Name, FiggleFont? Font);
}
92 changes: 92 additions & 0 deletions Figgle.Fonts/Aliases.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
1row,OneRow
3-d,ThreeD
3d_diagonal,ThreeDDiagonal
3x5,ThreeByFive
4max,FourMax
5lineoblique,FiveLineOblique
amc3line,Amc3Line
amc3liv1,Amc3Liv1
amcaaa01,AmcAaa01
amcneko,AmcNeko
amcrazo2,AmcRazor2
amcrazor,AmcRazor
amcslash,AmcSlash
amcslder,AmcSlder
amcthin,AmcThin
amctubes,AmcTubes
amcun1,AmcUn1
barbwire,BarbWire
bigchief,BigChief
bigfig,BigFig
broadway_kb,BroadwayKB
calgphy2,Caligraphy2
catwalk,CatWalk
cyberlarge,CyberLarge
cybermedium,CyberMedium
cybersmall,CyberSmall
dancingfont,DancingFont
defleppard,DefLeppard
dietcola,DietCola
dosrebel,DosRebel
dotmatrix,DotMatrix
doubleshorts,DoubleShorts
drpepper,DRPepper
dwhistled,DWhistled
eftichess,EftiChess
eftifont,EftiFont
eftipiti,EftiPiti
eftirobot,EftiRobot
eftitalic,EftiItalic
eftiwall,EftiWall
eftiwater,EftiWater
flowerpower,FlowerPower
fourtops,FourTops
funface,FunFace
funfaces,FunFaces
georgi16,Georgia16
Georgia11,Georgia11
graffiti,Graffiti
henry3d,Henry3d
horizontalleft,HorizontalLeft
horizontalright,HorizontalRight
impossible,Impossible
kontoslant,KontoSlant
larry3d,Larry3d
lildevil,LilDevil
lineblocks,LineBlocks
lockergnome,LockerGnome
maxfour,MaxFour
mshebrew210,Mshebrew210
nancyj,NancyJ
nancyj-fancy,NancyJFancy
nancyj-improved,NancyJImproved
nancyj-underlined,NancyJUnderlined
nscript,NScript
ntgreek,NTGreek
nvscript,NVScript
oldbanner,OldBanner
os2,OS2
ogre,Ogre
peaksslant,PeaksSlant
rectangles,Rectangles
rowancap,RowanCap
santaclara,SantaClara
sblood,SBlood
slant,Slant
slscript,ScriptSlant
serifcap,SerifCap
smallcaps,SmallCaps
smisome1,IsometricSmall
smkeyboard,KeyboardSmall
smpoison,PoisonSmall
smscript,ScriptSmall
smshadow,ShadowSmall
smslant,SlantSmall
smtengwar,TengwarSmall
standard,Standard
threepoint,ThreePoint
ticksslant,TicksSlant
tinker-toy,TinkerToy
twopoint,TwoPoint
usaflag,UsaFlag
wetletter,WetLetter
17 changes: 17 additions & 0 deletions Figgle.Fonts/Figgle.Fonts.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<EmbeddedResource Include="$(FontsZipFile)" LogicalName="Figgle.Fonts.zip" />
</ItemGroup>

<ItemGroup Label="Internal source generator configurations">
<AdditionalFiles Include="Aliases.csv" />
<AdditionalFiles Include="@(FontFiles)" Visible="false" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Figgle.Tests" />
<InternalsVisibleTo Include="Figgle.Generator" />
Expand All @@ -33,6 +38,18 @@
<ProjectReference Include="..\Figgle\Figgle.csproj" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Figgle.Fonts.Generator\Figgle.Fonts.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

<!--
Figgle.Fonts.Generator depends on Figgle to generate sample text documentation, so we must include it as an "analyzer" to make it available
to the generator.

Note that this project *also* has a runtime dependency to Figgle, which we set as a normal ProjectReference above. The two do not conflict.
-->
<ProjectReference Include="..\Figgle\Figgle.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

<!-- Create the zip file before build -->
<Target Name="CreateFontsZip" BeforeTargets="PrepareForBuild" Inputs="@(FontFiles)" Outputs="$(FontsZipFile)" Condition="'$(DesignTimeBuild)' != 'true' AND '$(BuildingProject)' == 'true'">

Expand Down
Loading