-
-
Notifications
You must be signed in to change notification settings - Fork 0
Add FLLUseDependencyInjection property group & Move all constants to Shared project #18
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using Flow.Launcher.Localization.Shared; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
@@ -7,7 +8,7 @@ | |
|
||
namespace Flow.Launcher.Localization.Analyzers.Localize | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
Check warning on line 11 in Flow.Launcher.Localization.Analyzers/Localize/ContextAvailabilityAnalyzer.cs
|
||
public class ContextAvailabilityAnalyzer : DiagnosticAnalyzer | ||
{ | ||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create( | ||
|
@@ -17,10 +18,6 @@ | |
AnalyzerDiagnostics.ContextIsNotDeclared | ||
); | ||
|
||
private const string PluginContextTypeName = "PluginInitContext"; | ||
|
||
private const string PluginInterfaceName = "IPluginI18n"; | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); | ||
|
@@ -30,6 +27,12 @@ | |
|
||
private static void AnalyzeNode(SyntaxNodeAnalysisContext context) | ||
{ | ||
var configOptions = context.Options.AnalyzerConfigOptionsProvider; | ||
var useDI = configOptions.GetFLLUseDependencyInjection(); | ||
|
||
// If we use dependency injection, we don't need to check for this context property | ||
if (useDI) return; | ||
|
||
var classDeclaration = (ClassDeclarationSyntax)context.Node; | ||
var semanticModel = context.SemanticModel; | ||
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); | ||
|
@@ -38,7 +41,7 @@ | |
|
||
var contextProperty = classDeclaration.Members.OfType<PropertyDeclarationSyntax>() | ||
.Select(p => semanticModel.GetDeclaredSymbol(p)) | ||
.FirstOrDefault(p => p?.Type.Name is PluginContextTypeName); | ||
.FirstOrDefault(p => p?.Type.Name is Constants.PluginContextTypeName); | ||
|
||
if (contextProperty != null) | ||
{ | ||
|
@@ -67,7 +70,7 @@ | |
.OfType<FieldDeclarationSyntax>() | ||
.SelectMany(f => f.Declaration.Variables) | ||
.Select(f => semanticModel.GetDeclaredSymbol(f)) | ||
.FirstOrDefault(f => f is IFieldSymbol fs && fs.Type.Name is PluginContextTypeName); | ||
.FirstOrDefault(f => f is IFieldSymbol fs && fs.Type.Name is Constants.PluginContextTypeName); | ||
var parentSyntax = fieldDeclaration | ||
?.DeclaringSyntaxReferences[0] | ||
.GetSyntax() | ||
|
@@ -89,6 +92,6 @@ | |
} | ||
|
||
private static bool IsPluginEntryClass(INamedTypeSymbol namedTypeSymbol) => | ||
namedTypeSymbol?.Interfaces.Any(i => i.Name == PluginInterfaceName) ?? false; | ||
namedTypeSymbol?.Interfaces.Any(i => i.Name == Constants.PluginInterfaceName) ?? false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using System.Text.RegularExpressions; | ||
|
||
namespace Flow.Launcher.Localization.Shared | ||
{ | ||
public static class Constants | ||
{ | ||
public const string DefaultNamespace = "Flow.Launcher"; | ||
public const string ClassName = "Localize"; | ||
public const string PluginInterfaceName = "IPluginI18n"; | ||
public const string PluginContextTypeName = "PluginInitContext"; | ||
public const string SystemPrefixUri = "clr-namespace:System;assembly=mscorlib"; | ||
public const string XamlPrefixUri = "http://schemas.microsoft.com/winfx/2006/xaml"; | ||
public const string XamlTag = "String"; | ||
public const string KeyTag = "Key"; | ||
|
||
public static readonly Regex LanguagesXamlRegex = new Regex(@"\\Languages\\[^\\]+\.xaml$", RegexOptions.IgnoreCase); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<Version>0.0.1</Version> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
<RootNamespace>Flow.Launcher.Localization.Shared</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Flow.Launcher.Localization.Shared | ||
{ | ||
public static class Helper | ||
{ | ||
public static bool GetFLLUseDependencyInjection(this AnalyzerConfigOptionsProvider configOptions) | ||
{ | ||
if (!configOptions.GlobalOptions.TryGetValue("build_property.FLLUseDependencyInjection", out var result) || | ||
!bool.TryParse(result, out var useDI)) | ||
{ | ||
return false; // Default to false | ||
} | ||
return useDI; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,13 @@ | |
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using System.Threading; | ||
using System.Xml.Linq; | ||
using Flow.Launcher.Localization.Shared; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Text; | ||
|
||
namespace Flow.Launcher.Localization.SourceGenerators.Localize | ||
|
@@ -21,19 +22,6 @@ public partial class LocalizeSourceGenerator : IIncrementalGenerator | |
{ | ||
#region Fields | ||
|
||
private const string CoreNamespace1 = "Flow.Launcher"; | ||
private const string CoreNamespace2 = "Flow.Launcher.Core"; | ||
private const string DefaultNamespace = "Flow.Launcher"; | ||
private const string ClassName = "Localize"; | ||
private const string PluginInterfaceName = "IPluginI18n"; | ||
private const string PluginContextTypeName = "PluginInitContext"; | ||
private const string systemPrefixUri = "clr-namespace:System;assembly=mscorlib"; | ||
private const string xamlPrefixUri = "http://schemas.microsoft.com/winfx/2006/xaml"; | ||
private const string XamlTag = "String"; | ||
private const string KeyTag = "Key"; | ||
|
||
private static readonly Regex _languagesXamlRegex = new Regex(@"\\Languages\\[^\\]+\.xaml$", RegexOptions.IgnoreCase); | ||
|
||
private static readonly Version PackageVersion = typeof(LocalizeSourceGenerator).Assembly.GetName().Version; | ||
|
||
private static readonly ImmutableArray<LocalizableString> _emptyLocalizableStrings = ImmutableArray<LocalizableString>.Empty; | ||
|
@@ -50,7 +38,7 @@ public partial class LocalizeSourceGenerator : IIncrementalGenerator | |
public void Initialize(IncrementalGeneratorInitializationContext context) | ||
{ | ||
var xamlFiles = context.AdditionalTextsProvider | ||
.Where(file => _languagesXamlRegex.IsMatch(file.Path)); | ||
.Where(file => Constants.LanguagesXamlRegex.IsMatch(file.Path)); | ||
|
||
var localizedStrings = xamlFiles | ||
.Select((file, ct) => ParseXamlFile(file, ct)) | ||
|
@@ -75,7 +63,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) | |
|
||
var compilation = context.CompilationProvider; | ||
|
||
var combined = localizedStrings.Combine(invocationKeys).Combine(pluginClasses).Combine(compilation).Combine(xamlFiles.Collect()); | ||
var configOptions = context.AnalyzerConfigOptionsProvider; | ||
|
||
var combined = localizedStrings.Combine(invocationKeys).Combine(pluginClasses).Combine(configOptions).Combine(compilation).Combine(xamlFiles.Collect()); | ||
|
||
context.RegisterSourceOutput(combined, Execute); | ||
} | ||
|
@@ -86,10 +76,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) | |
/// <param name="spc">The source production context.</param> | ||
/// <param name="data">The provided data.</param> | ||
private void Execute(SourceProductionContext spc, | ||
((((ImmutableArray<LocalizableString> LocalizableStrings, | ||
ImmutableHashSet<string> InvocationKeys), | ||
ImmutableArray<PluginClassInfo> PluginClassInfos), | ||
Compilation Compilation), | ||
(((((ImmutableArray<LocalizableString> LocalizableStrings, | ||
ImmutableHashSet<string> InvocationKeys), | ||
ImmutableArray<PluginClassInfo> PluginClassInfos), | ||
AnalyzerConfigOptionsProvider ConfigOptionsProvider), | ||
Compilation Compilation), | ||
ImmutableArray<AdditionalText> AdditionalTexts) data) | ||
{ | ||
var xamlFiles = data.AdditionalTexts; | ||
|
@@ -103,23 +94,28 @@ private void Execute(SourceProductionContext spc, | |
} | ||
|
||
var compilation = data.Item1.Compilation; | ||
var pluginClasses = data.Item1.Item1.PluginClassInfos; | ||
var usedKeys = data.Item1.Item1.Item1.InvocationKeys; | ||
var localizedStrings = data.Item1.Item1.Item1.LocalizableStrings; | ||
var configOptions = data.Item1.Item1.ConfigOptionsProvider; | ||
var pluginClasses = data.Item1.Item1.Item1.PluginClassInfos; | ||
var usedKeys = data.Item1.Item1.Item1.Item1.InvocationKeys; | ||
var localizedStrings = data.Item1.Item1.Item1.Item1.LocalizableStrings; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think those There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really do not know how to name those things, like Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was just curious if it's possible to avoid using tuples altogether and just use a record: private void Execute(SourceProductionContext context, GeneratorInputs data)
{ I don't have in-depth knowledge about source generators, so I don't know if it's possible or not. Let's leave it as is for now, and just remember that this is something that should probably change in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that .NetCore2.0 cannot support |
||
|
||
var assemblyName = compilation.AssemblyName ?? DefaultNamespace; | ||
var assemblyName = compilation.AssemblyName ?? Constants.DefaultNamespace; | ||
var optimizationLevel = compilation.Options.OptimizationLevel; | ||
var useDI = configOptions.GetFLLUseDependencyInjection(); | ||
|
||
var pluginInfo = GetValidPluginInfo(pluginClasses, spc); | ||
var isCoreAssembly = assemblyName == CoreNamespace1 || assemblyName == CoreNamespace2; | ||
|
||
PluginClassInfo pluginInfo = null; | ||
if (!useDI) | ||
{ | ||
pluginInfo = GetValidPluginInfo(pluginClasses, spc); | ||
} | ||
|
||
GenerateSource( | ||
spc, | ||
xamlFiles[0], | ||
localizedStrings, | ||
optimizationLevel, | ||
assemblyName, | ||
isCoreAssembly, | ||
useDI, | ||
pluginInfo, | ||
usedKeys); | ||
} | ||
|
@@ -155,11 +151,11 @@ private static ImmutableArray<LocalizableString> ParseXamlFile(AdditionalText fi | |
string uri = attr.Value; | ||
string prefix = attr.Name.LocalName; | ||
|
||
if (uri == systemPrefixUri) | ||
if (uri == Constants.SystemPrefixUri) | ||
{ | ||
systemPrefix = prefix; | ||
} | ||
else if (uri == xamlPrefixUri) | ||
else if (uri == Constants.XamlPrefixUri) | ||
{ | ||
xamlPrefix = prefix; | ||
} | ||
|
@@ -179,14 +175,14 @@ private static ImmutableArray<LocalizableString> ParseXamlFile(AdditionalText fi | |
} | ||
|
||
var localizableStrings = new List<LocalizableString>(); | ||
foreach (var element in doc.Descendants(systemNs + XamlTag)) // "String" elements in system namespace | ||
foreach (var element in doc.Descendants(systemNs + Constants.XamlTag)) // "String" elements in system namespace | ||
{ | ||
if (ct.IsCancellationRequested) | ||
{ | ||
return _emptyLocalizableStrings; | ||
} | ||
|
||
var key = element.Attribute(xNs + KeyTag)?.Value; // "Key" attribute in xaml namespace | ||
var key = element.Attribute(xNs + Constants.KeyTag)?.Value; // "Key" attribute in xaml namespace | ||
var value = element.Value; | ||
var comment = element.PreviousNode as XComment; | ||
|
||
|
@@ -424,7 +420,7 @@ private static string GetLocalizationKeyFromInvocation(GeneratorSyntaxContext co | |
parts.Reverse(); | ||
|
||
// Check if the first part is ClassName and there's at least one more part | ||
if (parts.Count < 2 || parts[0] != ClassName) | ||
if (parts.Count < 2 || parts[0] != Constants.ClassName) | ||
{ | ||
return null; | ||
} | ||
|
@@ -440,15 +436,15 @@ private static PluginClassInfo GetPluginClassInfo(GeneratorSyntaxContext context | |
{ | ||
var classDecl = (ClassDeclarationSyntax)context.Node; | ||
var location = GetLocation(context.SemanticModel.SyntaxTree, classDecl); | ||
if (!classDecl.BaseList?.Types.Any(t => t.Type.ToString() == PluginInterfaceName) ?? true) | ||
if (!classDecl.BaseList?.Types.Any(t => t.Type.ToString() == Constants.PluginInterfaceName) ?? true) | ||
{ | ||
// Cannot find class that implements IPluginI18n | ||
return null; | ||
} | ||
|
||
var property = classDecl.Members | ||
.OfType<PropertyDeclarationSyntax>() | ||
.FirstOrDefault(p => p.Type.ToString() == PluginContextTypeName); | ||
.FirstOrDefault(p => p.Type.ToString() == Constants.PluginContextTypeName); | ||
if (property is null) | ||
{ | ||
// Cannot find context | ||
|
@@ -532,7 +528,7 @@ private static void GenerateSource( | |
ImmutableArray<LocalizableString> localizedStrings, | ||
OptimizationLevel optimizationLevel, | ||
string assemblyName, | ||
bool isCoreAssembly, | ||
bool useDI, | ||
PluginClassInfo pluginInfo, | ||
IEnumerable<string> usedKeys) | ||
{ | ||
|
@@ -560,13 +556,6 @@ private static void GenerateSource( | |
GeneratedHeaderFromPath(sourceBuilder, xamlFile.Path); | ||
sourceBuilder.AppendLine(); | ||
|
||
// Generate usings | ||
if (isCoreAssembly) | ||
{ | ||
sourceBuilder.AppendLine("using Flow.Launcher.Core.Resource;"); | ||
sourceBuilder.AppendLine(); | ||
} | ||
|
||
// Generate nullable enable | ||
sourceBuilder.AppendLine("#nullable enable"); | ||
sourceBuilder.AppendLine(); | ||
|
@@ -603,11 +592,26 @@ private static void GenerateSource( | |
|
||
// Generate class | ||
sourceBuilder.AppendLine($"[System.CodeDom.Compiler.GeneratedCode(\"{nameof(LocalizeSourceGenerator)}\", \"{PackageVersion}\")]"); | ||
sourceBuilder.AppendLine($"public static class {ClassName}"); | ||
sourceBuilder.AppendLine($"public static class {Constants.ClassName}"); | ||
sourceBuilder.AppendLine("{"); | ||
|
||
// Generate localization methods | ||
var tabString = Spacing(1); | ||
|
||
// Generate API instance | ||
string getTranslation = null; | ||
if (useDI) | ||
{ | ||
sourceBuilder.AppendLine($"{tabString}private static Flow.Launcher.Plugin.IPublicAPI? api = null;"); | ||
sourceBuilder.AppendLine($"{tabString}private static Flow.Launcher.Plugin.IPublicAPI Api => api ??= CommunityToolkit.Mvvm.DependencyInjection.Ioc.Default.GetRequiredService<Flow.Launcher.Plugin.IPublicAPI>();"); | ||
sourceBuilder.AppendLine(); | ||
getTranslation = "Api.GetTranslation"; | ||
} | ||
else if (pluginInfo?.IsValid == true) | ||
{ | ||
getTranslation = $"{pluginInfo.ContextAccessor}.API.GetTranslation"; | ||
} | ||
|
||
// Generate localization methods | ||
foreach (var ls in localizedStrings) | ||
{ | ||
// TODO: Add support for usedKeys | ||
|
@@ -617,13 +621,13 @@ private static void GenerateSource( | |
}*/ | ||
|
||
GenerateDocComments(sourceBuilder, ls, tabString); | ||
GenerateLocalizationMethod(sourceBuilder, ls, isCoreAssembly, pluginInfo, tabString); | ||
GenerateLocalizationMethod(sourceBuilder, ls, getTranslation, tabString); | ||
} | ||
|
||
sourceBuilder.AppendLine("}"); | ||
|
||
// Add source to context | ||
spc.AddSource($"{ClassName}.{assemblyName}.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); | ||
spc.AddSource($"{Constants.ClassName}.{assemblyName}.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); | ||
} | ||
|
||
private static void GeneratedHeaderFromPath(StringBuilder sb, string xamlFilePath) | ||
|
@@ -673,8 +677,7 @@ private static void GenerateDocComments(StringBuilder sb, LocalizableString ls, | |
private static void GenerateLocalizationMethod( | ||
StringBuilder sb, | ||
LocalizableString ls, | ||
bool isCoreAssembly, | ||
PluginClassInfo pluginInfo, | ||
string getTranslation, | ||
string tabString) | ||
{ | ||
sb.Append($"{tabString}public static string {ls.Key}("); | ||
|
@@ -687,18 +690,8 @@ private static void GenerateLocalizationMethod( | |
? $", {string.Join(", ", parameters.Select(p => p.Name))}" | ||
: string.Empty; | ||
|
||
if (isCoreAssembly) | ||
{ | ||
var getTranslation = "InternationalizationManager.Instance.GetTranslation"; | ||
sb.AppendLine(parameters.Count > 0 | ||
? !ls.Format ? | ||
$"string.Format({getTranslation}(\"{ls.Key}\"){formatArgs});" | ||
: $"string.Format(System.Globalization.CultureInfo.CurrentCulture, {getTranslation}(\"{ls.Key}\"){formatArgs});" | ||
: $"{getTranslation}(\"{ls.Key}\");"); | ||
} | ||
else if (pluginInfo?.IsValid == true) | ||
if (!(string.IsNullOrEmpty(getTranslation))) | ||
{ | ||
var getTranslation = $"{pluginInfo.ContextAccessor}.API.GetTranslation"; | ||
sb.AppendLine(parameters.Count > 0 | ||
? !ls.Format ? | ||
$"string.Format({getTranslation}(\"{ls.Key}\"){formatArgs});" | ||
|
Uh oh!
There was an error while loading. Please reload this page.