Skip to content

Commit 61fed01

Browse files
authored
Merge pull request #18 from Flow-Launcher/use_dependency_injection
Add FLLUseDependencyInjection property group & Move all constants to Shared project
2 parents f8496ab + fdf2cb1 commit 61fed01

11 files changed

+166
-102
lines changed

Flow.Launcher.Localization.Analyzers/Flow.Launcher.Localization.Analyzers.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@
1616
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
1717
</ItemGroup>
1818

19+
<ItemGroup>
20+
<ProjectReference Include="..\Flow.Launcher.Localization.Shared\Flow.Launcher.Localization.Shared.csproj" />
21+
</ItemGroup>
22+
1923
</Project>

Flow.Launcher.Localization.Analyzers/Localize/ContextAvailabilityAnalyzer.cs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Immutable;
22
using System.Linq;
3+
using Flow.Launcher.Localization.Shared;
34
using Microsoft.CodeAnalysis;
45
using Microsoft.CodeAnalysis.CSharp;
56
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -17,10 +18,6 @@ public class ContextAvailabilityAnalyzer : DiagnosticAnalyzer
1718
AnalyzerDiagnostics.ContextIsNotDeclared
1819
);
1920

20-
private const string PluginContextTypeName = "PluginInitContext";
21-
22-
private const string PluginInterfaceName = "IPluginI18n";
23-
2421
public override void Initialize(AnalysisContext context)
2522
{
2623
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
@@ -30,6 +27,12 @@ public override void Initialize(AnalysisContext context)
3027

3128
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
3229
{
30+
var configOptions = context.Options.AnalyzerConfigOptionsProvider;
31+
var useDI = configOptions.GetFLLUseDependencyInjection();
32+
33+
// If we use dependency injection, we don't need to check for this context property
34+
if (useDI) return;
35+
3336
var classDeclaration = (ClassDeclarationSyntax)context.Node;
3437
var semanticModel = context.SemanticModel;
3538
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration);
@@ -38,7 +41,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
3841

3942
var contextProperty = classDeclaration.Members.OfType<PropertyDeclarationSyntax>()
4043
.Select(p => semanticModel.GetDeclaredSymbol(p))
41-
.FirstOrDefault(p => p?.Type.Name is PluginContextTypeName);
44+
.FirstOrDefault(p => p?.Type.Name is Constants.PluginContextTypeName);
4245

4346
if (contextProperty != null)
4447
{
@@ -67,7 +70,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
6770
.OfType<FieldDeclarationSyntax>()
6871
.SelectMany(f => f.Declaration.Variables)
6972
.Select(f => semanticModel.GetDeclaredSymbol(f))
70-
.FirstOrDefault(f => f is IFieldSymbol fs && fs.Type.Name is PluginContextTypeName);
73+
.FirstOrDefault(f => f is IFieldSymbol fs && fs.Type.Name is Constants.PluginContextTypeName);
7174
var parentSyntax = fieldDeclaration
7275
?.DeclaringSyntaxReferences[0]
7376
.GetSyntax()
@@ -89,6 +92,6 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
8992
}
9093

9194
private static bool IsPluginEntryClass(INamedTypeSymbol namedTypeSymbol) =>
92-
namedTypeSymbol?.Interfaces.Any(i => i.Name == PluginInterfaceName) ?? false;
95+
namedTypeSymbol?.Interfaces.Any(i => i.Name == Constants.PluginInterfaceName) ?? false;
9396
}
9497
}

Flow.Launcher.Localization.Analyzers/Localize/OldGetTranslateAnalyzerCodeFixProvider.cs

-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,5 @@ InvocationExpressionSyntax invocationExpr
109109
var newRoot = root.ReplaceNode(invocationExpr, newInnerInvocationExpr);
110110
return context.Document.WithSyntaxRoot(newRoot);
111111
}
112-
113112
}
114113
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace Flow.Launcher.Localization.Shared
4+
{
5+
public static class Constants
6+
{
7+
public const string DefaultNamespace = "Flow.Launcher";
8+
public const string ClassName = "Localize";
9+
public const string PluginInterfaceName = "IPluginI18n";
10+
public const string PluginContextTypeName = "PluginInitContext";
11+
public const string SystemPrefixUri = "clr-namespace:System;assembly=mscorlib";
12+
public const string XamlPrefixUri = "http://schemas.microsoft.com/winfx/2006/xaml";
13+
public const string XamlTag = "String";
14+
public const string KeyAttribute = "Key";
15+
16+
public static readonly Regex LanguagesXamlRegex = new Regex(@"\\Languages\\[^\\]+\.xaml$", RegexOptions.IgnoreCase);
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Version>0.0.1</Version>
5+
<TargetFramework>netstandard2.0</TargetFramework>
6+
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
7+
<RootNamespace>Flow.Launcher.Localization.Shared</RootNamespace>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" />
12+
</ItemGroup>
13+
14+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.CodeAnalysis.Diagnostics;
2+
3+
namespace Flow.Launcher.Localization.Shared
4+
{
5+
public static class Helper
6+
{
7+
public static bool GetFLLUseDependencyInjection(this AnalyzerConfigOptionsProvider configOptions)
8+
{
9+
if (!configOptions.GlobalOptions.TryGetValue("build_property.FLLUseDependencyInjection", out var result) ||
10+
!bool.TryParse(result, out var useDI))
11+
{
12+
return false; // Default to false
13+
}
14+
return useDI;
15+
}
16+
}
17+
}

Flow.Launcher.Localization.SourceGenerators/Flow.Launcher.Localization.SourceGenerators.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@
1414
</PackageReference>
1515
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
1616
</ItemGroup>
17+
18+
<ItemGroup>
19+
<ProjectReference Include="..\Flow.Launcher.Localization.Shared\Flow.Launcher.Localization.Shared.csproj" />
20+
</ItemGroup>
1721

1822
</Project>

Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs

+53-60
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
using System.Collections.Immutable;
44
using System.Linq;
55
using System.Text;
6-
using System.Text.RegularExpressions;
76
using System.Threading;
87
using System.Xml.Linq;
8+
using Flow.Launcher.Localization.Shared;
99
using Microsoft.CodeAnalysis;
1010
using Microsoft.CodeAnalysis.CSharp;
1111
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using Microsoft.CodeAnalysis.Diagnostics;
1213
using Microsoft.CodeAnalysis.Text;
1314

1415
namespace Flow.Launcher.Localization.SourceGenerators.Localize
@@ -21,19 +22,6 @@ public partial class LocalizeSourceGenerator : IIncrementalGenerator
2122
{
2223
#region Fields
2324

24-
private const string CoreNamespace1 = "Flow.Launcher";
25-
private const string CoreNamespace2 = "Flow.Launcher.Core";
26-
private const string DefaultNamespace = "Flow.Launcher";
27-
private const string ClassName = "Localize";
28-
private const string PluginInterfaceName = "IPluginI18n";
29-
private const string PluginContextTypeName = "PluginInitContext";
30-
private const string systemPrefixUri = "clr-namespace:System;assembly=mscorlib";
31-
private const string xamlPrefixUri = "http://schemas.microsoft.com/winfx/2006/xaml";
32-
private const string XamlTag = "String";
33-
private const string KeyTag = "Key";
34-
35-
private static readonly Regex _languagesXamlRegex = new Regex(@"\\Languages\\[^\\]+\.xaml$", RegexOptions.IgnoreCase);
36-
3725
private static readonly Version PackageVersion = typeof(LocalizeSourceGenerator).Assembly.GetName().Version;
3826

3927
private static readonly ImmutableArray<LocalizableString> _emptyLocalizableStrings = ImmutableArray<LocalizableString>.Empty;
@@ -50,7 +38,7 @@ public partial class LocalizeSourceGenerator : IIncrementalGenerator
5038
public void Initialize(IncrementalGeneratorInitializationContext context)
5139
{
5240
var xamlFiles = context.AdditionalTextsProvider
53-
.Where(file => _languagesXamlRegex.IsMatch(file.Path));
41+
.Where(file => Constants.LanguagesXamlRegex.IsMatch(file.Path));
5442

5543
var localizedStrings = xamlFiles
5644
.Select((file, ct) => ParseXamlFile(file, ct))
@@ -75,7 +63,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
7563

7664
var compilation = context.CompilationProvider;
7765

78-
var combined = localizedStrings.Combine(invocationKeys).Combine(pluginClasses).Combine(compilation).Combine(xamlFiles.Collect());
66+
var configOptions = context.AnalyzerConfigOptionsProvider;
67+
68+
var combined = localizedStrings.Combine(invocationKeys).Combine(pluginClasses).Combine(configOptions).Combine(compilation).Combine(xamlFiles.Collect());
7969

8070
context.RegisterSourceOutput(combined, Execute);
8171
}
@@ -86,10 +76,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
8676
/// <param name="spc">The source production context.</param>
8777
/// <param name="data">The provided data.</param>
8878
private void Execute(SourceProductionContext spc,
89-
((((ImmutableArray<LocalizableString> LocalizableStrings,
90-
ImmutableHashSet<string> InvocationKeys),
91-
ImmutableArray<PluginClassInfo> PluginClassInfos),
92-
Compilation Compilation),
79+
(((((ImmutableArray<LocalizableString> LocalizableStrings,
80+
ImmutableHashSet<string> InvocationKeys),
81+
ImmutableArray<PluginClassInfo> PluginClassInfos),
82+
AnalyzerConfigOptionsProvider ConfigOptionsProvider),
83+
Compilation Compilation),
9384
ImmutableArray<AdditionalText> AdditionalTexts) data)
9485
{
9586
var xamlFiles = data.AdditionalTexts;
@@ -103,23 +94,28 @@ private void Execute(SourceProductionContext spc,
10394
}
10495

10596
var compilation = data.Item1.Compilation;
106-
var pluginClasses = data.Item1.Item1.PluginClassInfos;
107-
var usedKeys = data.Item1.Item1.Item1.InvocationKeys;
108-
var localizedStrings = data.Item1.Item1.Item1.LocalizableStrings;
97+
var configOptions = data.Item1.Item1.ConfigOptionsProvider;
98+
var pluginClasses = data.Item1.Item1.Item1.PluginClassInfos;
99+
var usedKeys = data.Item1.Item1.Item1.Item1.InvocationKeys;
100+
var localizedStrings = data.Item1.Item1.Item1.Item1.LocalizableStrings;
109101

110-
var assemblyName = compilation.AssemblyName ?? DefaultNamespace;
102+
var assemblyName = compilation.AssemblyName ?? Constants.DefaultNamespace;
111103
var optimizationLevel = compilation.Options.OptimizationLevel;
104+
var useDI = configOptions.GetFLLUseDependencyInjection();
112105

113-
var pluginInfo = GetValidPluginInfo(pluginClasses, spc);
114-
var isCoreAssembly = assemblyName == CoreNamespace1 || assemblyName == CoreNamespace2;
115-
106+
PluginClassInfo pluginInfo = null;
107+
if (!useDI)
108+
{
109+
pluginInfo = GetValidPluginInfo(pluginClasses, spc);
110+
}
111+
116112
GenerateSource(
117113
spc,
118114
xamlFiles[0],
119115
localizedStrings,
120116
optimizationLevel,
121117
assemblyName,
122-
isCoreAssembly,
118+
useDI,
123119
pluginInfo,
124120
usedKeys);
125121
}
@@ -155,11 +151,11 @@ private static ImmutableArray<LocalizableString> ParseXamlFile(AdditionalText fi
155151
string uri = attr.Value;
156152
string prefix = attr.Name.LocalName;
157153

158-
if (uri == systemPrefixUri)
154+
if (uri == Constants.SystemPrefixUri)
159155
{
160156
systemPrefix = prefix;
161157
}
162-
else if (uri == xamlPrefixUri)
158+
else if (uri == Constants.XamlPrefixUri)
163159
{
164160
xamlPrefix = prefix;
165161
}
@@ -179,14 +175,14 @@ private static ImmutableArray<LocalizableString> ParseXamlFile(AdditionalText fi
179175
}
180176

181177
var localizableStrings = new List<LocalizableString>();
182-
foreach (var element in doc.Descendants(systemNs + XamlTag)) // "String" elements in system namespace
178+
foreach (var element in doc.Descendants(systemNs + Constants.XamlTag)) // "String" elements in system namespace
183179
{
184180
if (ct.IsCancellationRequested)
185181
{
186182
return _emptyLocalizableStrings;
187183
}
188184

189-
var key = element.Attribute(xNs + KeyTag)?.Value; // "Key" attribute in xaml namespace
185+
var key = element.Attribute(xNs + Constants.KeyAttribute)?.Value; // "Key" attribute in xaml namespace
190186
var value = element.Value;
191187
var comment = element.PreviousNode as XComment;
192188

@@ -424,7 +420,7 @@ private static string GetLocalizationKeyFromInvocation(GeneratorSyntaxContext co
424420
parts.Reverse();
425421

426422
// Check if the first part is ClassName and there's at least one more part
427-
if (parts.Count < 2 || parts[0] != ClassName)
423+
if (parts.Count < 2 || parts[0] != Constants.ClassName)
428424
{
429425
return null;
430426
}
@@ -440,15 +436,15 @@ private static PluginClassInfo GetPluginClassInfo(GeneratorSyntaxContext context
440436
{
441437
var classDecl = (ClassDeclarationSyntax)context.Node;
442438
var location = GetLocation(context.SemanticModel.SyntaxTree, classDecl);
443-
if (!classDecl.BaseList?.Types.Any(t => t.Type.ToString() == PluginInterfaceName) ?? true)
439+
if (!classDecl.BaseList?.Types.Any(t => t.Type.ToString() == Constants.PluginInterfaceName) ?? true)
444440
{
445441
// Cannot find class that implements IPluginI18n
446442
return null;
447443
}
448444

449445
var property = classDecl.Members
450446
.OfType<PropertyDeclarationSyntax>()
451-
.FirstOrDefault(p => p.Type.ToString() == PluginContextTypeName);
447+
.FirstOrDefault(p => p.Type.ToString() == Constants.PluginContextTypeName);
452448
if (property is null)
453449
{
454450
// Cannot find context
@@ -532,7 +528,7 @@ private static void GenerateSource(
532528
ImmutableArray<LocalizableString> localizedStrings,
533529
OptimizationLevel optimizationLevel,
534530
string assemblyName,
535-
bool isCoreAssembly,
531+
bool useDI,
536532
PluginClassInfo pluginInfo,
537533
IEnumerable<string> usedKeys)
538534
{
@@ -560,13 +556,6 @@ private static void GenerateSource(
560556
GeneratedHeaderFromPath(sourceBuilder, xamlFile.Path);
561557
sourceBuilder.AppendLine();
562558

563-
// Generate usings
564-
if (isCoreAssembly)
565-
{
566-
sourceBuilder.AppendLine("using Flow.Launcher.Core.Resource;");
567-
sourceBuilder.AppendLine();
568-
}
569-
570559
// Generate nullable enable
571560
sourceBuilder.AppendLine("#nullable enable");
572561
sourceBuilder.AppendLine();
@@ -603,11 +592,26 @@ private static void GenerateSource(
603592

604593
// Generate class
605594
sourceBuilder.AppendLine($"[System.CodeDom.Compiler.GeneratedCode(\"{nameof(LocalizeSourceGenerator)}\", \"{PackageVersion}\")]");
606-
sourceBuilder.AppendLine($"public static class {ClassName}");
595+
sourceBuilder.AppendLine($"public static class {Constants.ClassName}");
607596
sourceBuilder.AppendLine("{");
608597

609-
// Generate localization methods
610598
var tabString = Spacing(1);
599+
600+
// Generate API instance
601+
string getTranslation = null;
602+
if (useDI)
603+
{
604+
sourceBuilder.AppendLine($"{tabString}private static Flow.Launcher.Plugin.IPublicAPI? api = null;");
605+
sourceBuilder.AppendLine($"{tabString}private static Flow.Launcher.Plugin.IPublicAPI Api => api ??= CommunityToolkit.Mvvm.DependencyInjection.Ioc.Default.GetRequiredService<Flow.Launcher.Plugin.IPublicAPI>();");
606+
sourceBuilder.AppendLine();
607+
getTranslation = "Api.GetTranslation";
608+
}
609+
else if (pluginInfo?.IsValid == true)
610+
{
611+
getTranslation = $"{pluginInfo.ContextAccessor}.API.GetTranslation";
612+
}
613+
614+
// Generate localization methods
611615
foreach (var ls in localizedStrings)
612616
{
613617
// TODO: Add support for usedKeys
@@ -617,13 +621,13 @@ private static void GenerateSource(
617621
}*/
618622

619623
GenerateDocComments(sourceBuilder, ls, tabString);
620-
GenerateLocalizationMethod(sourceBuilder, ls, isCoreAssembly, pluginInfo, tabString);
624+
GenerateLocalizationMethod(sourceBuilder, ls, getTranslation, tabString);
621625
}
622626

623627
sourceBuilder.AppendLine("}");
624628

625629
// Add source to context
626-
spc.AddSource($"{ClassName}.{assemblyName}.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
630+
spc.AddSource($"{Constants.ClassName}.{assemblyName}.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
627631
}
628632

629633
private static void GeneratedHeaderFromPath(StringBuilder sb, string xamlFilePath)
@@ -673,8 +677,7 @@ private static void GenerateDocComments(StringBuilder sb, LocalizableString ls,
673677
private static void GenerateLocalizationMethod(
674678
StringBuilder sb,
675679
LocalizableString ls,
676-
bool isCoreAssembly,
677-
PluginClassInfo pluginInfo,
680+
string getTranslation,
678681
string tabString)
679682
{
680683
sb.Append($"{tabString}public static string {ls.Key}(");
@@ -687,18 +690,8 @@ private static void GenerateLocalizationMethod(
687690
? $", {string.Join(", ", parameters.Select(p => p.Name))}"
688691
: string.Empty;
689692

690-
if (isCoreAssembly)
691-
{
692-
var getTranslation = "InternationalizationManager.Instance.GetTranslation";
693-
sb.AppendLine(parameters.Count > 0
694-
? !ls.Format ?
695-
$"string.Format({getTranslation}(\"{ls.Key}\"){formatArgs});"
696-
: $"string.Format(System.Globalization.CultureInfo.CurrentCulture, {getTranslation}(\"{ls.Key}\"){formatArgs});"
697-
: $"{getTranslation}(\"{ls.Key}\");");
698-
}
699-
else if (pluginInfo?.IsValid == true)
693+
if (!(string.IsNullOrEmpty(getTranslation)))
700694
{
701-
var getTranslation = $"{pluginInfo.ContextAccessor}.API.GetTranslation";
702695
sb.AppendLine(parameters.Count > 0
703696
? !ls.Format ?
704697
$"string.Format({getTranslation}(\"{ls.Key}\"){formatArgs});"

0 commit comments

Comments
 (0)