-
-
Notifications
You must be signed in to change notification settings - Fork 850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace ImageExtensions.Save.tt with Source Generator #2237
Open
CollinAlpert
wants to merge
3
commits into
SixLabors:main
Choose a base branch
from
CollinAlpert:source-generators
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+195
−995
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
165 changes: 165 additions & 0 deletions
165
src/ImageSharp.Generators/ImageExtensionsSaveGenerator.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,165 @@ | ||
// Copyright (c) Six Labors. | ||
// Licensed under the Six Labors Split License. | ||
|
||
using System.Text; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Text; | ||
|
||
namespace ImageSharp.Generators; | ||
|
||
[Generator(LanguageNames.CSharp)] | ||
internal class ImageExtensionsSaveGenerator : IIncrementalGenerator | ||
{ | ||
private const string FileHeader = @"// Copyright (c) Six Labors. | ||
// Licensed under the Six Labors Split License. | ||
|
||
// <auto-generated />"; | ||
|
||
private static readonly string[] ImageFormats = | ||
{ | ||
"Bmp", | ||
"Gif", | ||
"Jpeg", | ||
"Pbm", | ||
"Png", | ||
"Tga", | ||
"Webp", | ||
"Tiff" | ||
}; | ||
|
||
public void Initialize(IncrementalGeneratorInitializationContext context) => context.RegisterPostInitializationOutput(GenerateExtensionsMethods); | ||
|
||
private static void GenerateExtensionsMethods(IncrementalGeneratorPostInitializationContext ctx) | ||
{ | ||
StringBuilder stringBuilder = new(FileHeader); | ||
stringBuilder.AppendLine(); | ||
stringBuilder.AppendLine("using SixLabors.ImageSharp.Advanced;"); | ||
foreach (string format in ImageFormats) | ||
{ | ||
stringBuilder.Append("using SixLabors.ImageSharp.Formats.").Append(format).AppendLine(";"); | ||
} | ||
|
||
stringBuilder.AppendLine(); | ||
stringBuilder.AppendLine("namespace SixLabors.ImageSharp;"); | ||
stringBuilder.AppendLine(); | ||
stringBuilder.AppendLine(@"/// <summary> | ||
/// Extension methods for the <see cref=""Image""/> type. | ||
/// </summary> | ||
public static partial class ImageExtensions | ||
{"); | ||
|
||
ctx.CancellationToken.ThrowIfCancellationRequested(); | ||
|
||
foreach (string format in ImageFormats) | ||
{ | ||
string methods = $@" | ||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""path"">The file path to save the image to.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the path is null.</exception> | ||
public static void SaveAs{format}(this Image source, string path) => SaveAs{format}(source, path, null); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""path"">The file path to save the image to.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the path is null.</exception> | ||
/// <returns>A <see cref=""Task""/> representing the asynchronous operation.</returns> | ||
public static Task SaveAs{format}Async(this Image source, string path) => SaveAs{format}Async(source, path, null); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""path"">The file path to save the image to.</param> | ||
/// <param name=""cancellationToken"">The token to monitor for cancellation requests.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the path is null.</exception> | ||
/// <returns>A <see cref=""Task""/> representing the asynchronous operation.</returns> | ||
public static Task SaveAs{format}Async(this Image source, string path, CancellationToken cancellationToken) | ||
=> SaveAs{format}Async(source, path, null, cancellationToken); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""path"">The file path to save the image to.</param> | ||
/// <param name=""encoder"">The encoder to save the image with.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the path is null.</exception> | ||
public static void SaveAs{format}(this Image source, string path, {format}Encoder encoder) => | ||
source.Save( | ||
path, | ||
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder({format}Format.Instance)); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""path"">The file path to save the image to.</param> | ||
/// <param name=""encoder"">The encoder to save the image with.</param> | ||
/// <param name=""cancellationToken"">The token to monitor for cancellation requests.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the path is null.</exception> | ||
/// <returns>A <see cref=""Task""/> representing the asynchronous operation.</returns> | ||
public static Task SaveAs{format}Async(this Image source, string path, {format}Encoder encoder, CancellationToken cancellationToken = default) => | ||
source.SaveAsync( | ||
path, | ||
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder({format}Format.Instance), | ||
cancellationToken); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""stream"">The stream to save the image to.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the stream is null.</exception> | ||
public static void SaveAs{format}(this Image source, Stream stream) | ||
=> SaveAs{format}(source, stream, null); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""stream"">The stream to save the image to.</param> | ||
/// <param name=""cancellationToken"">The token to monitor for cancellation requests.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the stream is null.</exception> | ||
/// <returns>A <see cref=""Task""/> representing the asynchronous operation.</returns> | ||
public static Task SaveAs{format}Async(this Image source, Stream stream, CancellationToken cancellationToken = default) | ||
=> SaveAs{format}Async(source, stream, null, cancellationToken); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""stream"">The stream to save the image to.</param> | ||
/// <param name=""encoder"">The encoder to save the image with.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the stream is null.</exception> | ||
public static void SaveAs{format}(this Image source, Stream stream, {format}Encoder encoder) | ||
=> source.Save( | ||
stream, | ||
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder({format}Format.Instance)); | ||
|
||
/// <summary> | ||
/// Saves the image to the given stream with the {format} format. | ||
/// </summary> | ||
/// <param name=""source"">The image this method extends.</param> | ||
/// <param name=""stream"">The stream to save the image to.</param> | ||
/// <param name=""encoder"">The encoder to save the image with.</param> | ||
/// <param name=""cancellationToken"">The token to monitor for cancellation requests.</param> | ||
/// <exception cref=""System.ArgumentNullException"">Thrown if the stream is null.</exception> | ||
/// <returns>A <see cref=""Task""/> representing the asynchronous operation.</returns> | ||
public static Task SaveAs{format}Async(this Image source, Stream stream, {format}Encoder encoder, CancellationToken cancellationToken = default) => | ||
source.SaveAsync( | ||
stream, | ||
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder({format}Format.Instance), | ||
cancellationToken);"; | ||
stringBuilder.AppendLine(methods); | ||
} | ||
|
||
stringBuilder.Append('}'); | ||
|
||
SourceText sourceText = SourceText.From(stringBuilder.ToString(), Encoding.UTF8); | ||
ctx.AddSource("ImageExtensions.Save.g.cs", sourceText); | ||
} | ||
} |
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,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>netstandard2.0;net6.0;net7.0</TargetFrameworks> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<!-- Keep up to date with .NET version of ImageSharp --> | ||
<LangVersion>10</LangVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Using Remove="SixLabors" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to have some sort of attribute on the encoder type that can be used to generate the methods instead?
That would make it more generator worthy would it not @Sergio0694 ?
It would be amazing if we could add that to a base class (ImageEncoder See #2269) and have these methods generated automatically without us ever having to touch this file again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, this would be possible and probably a better suited scenario for a Source Generator. You'd still have the issues around local projects with SGs which @Sergio0694 mentioned in #2237 (comment) and would need to decide if this is okay for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one? I'm still blown away by that. How does someone test such a thing if locally referencing the project is unsupported? It seems incredibly short sighted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, to be honest I haven't noticed this as much. I rarely clean my repository like that.
I have multiple SGs in a local project at work and only get red squiggly lines when modifying the SG code, which rarely happens. Even then closing and reopening the solution fixes it. Other than that, all is good.