Browse Source

Merge branch 'v2_develop' into v2_2432-DimAuto

Tig 1 year ago
parent
commit
9f8f261034
65 changed files with 4984 additions and 0 deletions
  1. 21 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Program.cs
  2. 26 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj
  3. 48 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/EnumMemberValues.cs
  4. 49 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum.cs
  5. 49 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt.cs
  6. 50 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt_NoFastIsDefined.cs
  7. 49 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt.cs
  8. 49 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt_NoFastIsDefined.cs
  9. 49 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_NoFastIsDefined.cs
  10. 50 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum.cs
  11. 51 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitInt.cs
  12. 50 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitUInt.cs
  13. 46 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum.cs
  14. 48 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitInt.cs
  15. 46 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitUint.cs
  16. 43 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum.cs
  17. 43 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitInt.cs
  18. 43 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitUInt.cs
  19. 329 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGeneratorTests.cs
  20. 111 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/IndentedTextWriterExtensionsTests.cs
  21. 50 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj
  22. 3 0
      Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj.DotSettings
  23. 20 0
      Analyzers/Terminal.Gui.Analyzers.Internal/AccessibilityExtensions.cs
  24. 8 0
      Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Shipped.md
  25. 4 0
      Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Unshipped.md
  26. 117 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs
  27. 3 0
      Analyzers/Terminal.Gui.Analyzers.Internal/ApiCompatExcludedAttributes.txt
  28. 26 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs
  29. 22 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/CombinationGroupingAttribute.cs
  30. 37 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs
  31. 110 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs
  32. 110 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumMemberCombinationsAttribute.cs
  33. 14 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs
  34. 33 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/CompilerFeatureRequiredAttribute.cs
  35. 11 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs
  36. 6 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs
  37. 18 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IsExternalInit.cs
  38. 208 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NullableAttributes.cs
  39. 43 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs
  40. 12 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/RequiredMemberAttribute.cs
  41. 12 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/SetsRequiredMembersAttribute.cs
  42. 14 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/SkipLocalsInitAttribute.cs
  43. 202 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs
  44. 235 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs
  45. 457 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs
  46. 452 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs
  47. 130 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumMemberCombinationsGenerator.cs
  48. 38 0
      Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs
  49. 28 0
      Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs
  50. 71 0
      Analyzers/Terminal.Gui.Analyzers.Internal/IndentedTextWriterExtensions.cs
  51. 8 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Properties/launchSettings.json
  52. 98 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj
  53. 4 0
      Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj.DotSettings
  54. 6 0
      Directory.build.props
  55. 16 0
      Scripts/COPYRIGHT
  56. 46 0
      Scripts/README.md
  57. 105 0
      Scripts/Terminal.Gui.PowerShell.Analyzers.psm1
  58. 131 0
      Scripts/Terminal.Gui.PowerShell.Build.psd1
  59. 32 0
      Scripts/Terminal.Gui.PowerShell.Build.psm1
  60. 138 0
      Scripts/Terminal.Gui.PowerShell.Core.psd1
  61. 143 0
      Scripts/Terminal.Gui.PowerShell.Core.psm1
  62. 135 0
      Scripts/Terminal.Gui.PowerShell.Git.psd1
  63. 111 0
      Scripts/Terminal.Gui.PowerShell.Git.psm1
  64. 150 0
      Scripts/Terminal.Gui.PowerShell.psd1
  65. 117 0
      Scripts/Terminal.Gui.Powershell.Analyzers.psd1

+ 21 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Program.cs

@@ -0,0 +1,21 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Debugging;
+
+class Program
+{
+    static void Main (string [] args)
+    {
+        
+    }
+}
+
+[GenerateEnumExtensionMethods]
+public enum TestEnum
+{
+    Zero = 0,
+    One,
+    Two = 2,
+    Three,
+    Six = 6
+}

+ 26 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj

@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.9.2" PrivateAssets="all" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Terminal.Gui.Analyzers.Internal\Terminal.Gui.Analyzers.Internal.csproj">
+      <PrivateAssets>all</PrivateAssets>
+      <OutputItemType>Analyzer</OutputItemType>
+      <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+    </ProjectReference>
+  </ItemGroup>
+
+</Project>

+ 48 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/EnumMemberValues.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+internal class SignedEnumMemberValues
+{
+    internal const int Bit31 = ~0b_01111111_11111111_11111111_11111111;
+    internal const int Bit30 =  0b_01000000_00000000_00000000_00000000;
+    internal const int Bit29 =  0b_00100000_00000000_00000000_00000000;
+    internal const int Bit28 =  0b_00010000_00000000_00000000_00000000;
+    internal const int Bit27 =  0b_00001000_00000000_00000000_00000000;
+    internal const int Bit26 =  0b_00000100_00000000_00000000_00000000;
+    internal const int Bit25 =  0b_00000010_00000000_00000000_00000000;
+    internal const int Bit24 =  0b_00000001_00000000_00000000_00000000;
+    internal const int Bit23 =  0b_00000000_10000000_00000000_00000000;
+    internal const int Bit22 =  0b_00000000_01000000_00000000_00000000;
+    internal const int Bit21 =  0b_00000000_00100000_00000000_00000000;
+    internal const int Bit20 =  0b_00000000_00010000_00000000_00000000;
+    internal const int Bit19 =  0b_00000000_00001000_00000000_00000000;
+    internal const int Bit18 =  0b_00000000_00000100_00000000_00000000;
+    internal const int Bit17 =  0b_00000000_00000010_00000000_00000000;
+    internal const int Bit16 =  0b_00000000_00000001_00000000_00000000;
+    internal const int Bit15 =  0b_00000000_00000000_10000000_00000000;
+    internal const int Bit14 =  0b_00000000_00000000_01000000_00000000;
+    internal const int Bit13 =  0b_00000000_00000000_00100000_00000000;
+    internal const int Bit12 =  0b_00000000_00000000_00010000_00000000;
+    internal const int Bit11 =  0b_00000000_00000000_00001000_00000000;
+    internal const int Bit10 =  0b_00000000_00000000_00000100_00000000;
+    internal const int Bit09 =  0b_00000000_00000000_00000010_00000000;
+    internal const int Bit08 =  0b_00000000_00000000_00000001_00000000;
+    internal const int Bit07 =  0b_00000000_00000000_00000000_10000000;
+    internal const int Bit06 =  0b_00000000_00000000_00000000_01000000;
+    internal const int Bit05 =  0b_00000000_00000000_00000000_00100000;
+    internal const int Bit04 =  0b_00000000_00000000_00000000_00010000;
+    internal const int Bit03 =  0b_00000000_00000000_00000000_00001000;
+    internal const int Bit02 =  0b_00000000_00000000_00000000_00000100;
+    internal const int Bit01 =  0b_00000000_00000000_00000000_00000010;
+    internal const int Bit00 =  0b_00000000_00000000_00000000_00000001;
+    internal const int All_0 =  0;
+    internal const int All_1 =  ~All_0;
+    internal const int Alternating_01 = 0b_01010101_01010101_01010101_01010101;
+    internal const int Alternating_10 = ~Alternating_01;
+    internal const int EvenBytesHigh = 0b_00000000_11111111_00000000_11111111;
+    internal const int OddBytesHigh = ~EvenBytesHigh;
+}

+ 49 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum.cs

@@ -0,0 +1,49 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="BasicEnum"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute"/> applied.
+/// </summary>
+[GenerateEnumExtensionMethods]
+public enum BetterEnum
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 =  0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 =  0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 =  0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 49 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt.cs

@@ -0,0 +1,49 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="BasicEnum_ExplicitInt"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute"/> applied.
+/// </summary>
+[GenerateEnumExtensionMethods]
+public enum BetterEnum_ExplicitInt
+{
+    Bit31 = BasicEnum_ExplicitInt.Bit31,
+    Bit30 = BasicEnum_ExplicitInt.Bit30,
+    Bit29 = BasicEnum_ExplicitInt.Bit29,
+    Bit28 = BasicEnum_ExplicitInt.Bit28,
+    Bit27 = BasicEnum_ExplicitInt.Bit27,
+    Bit26 = BasicEnum_ExplicitInt.Bit26,
+    Bit25 = BasicEnum_ExplicitInt.Bit25,
+    Bit24 = BasicEnum_ExplicitInt.Bit24,
+    Bit23 = BasicEnum_ExplicitInt.Bit23,
+    Bit22 = BasicEnum_ExplicitInt.Bit22,
+    Bit21 = BasicEnum_ExplicitInt.Bit21,
+    Bit20 = BasicEnum_ExplicitInt.Bit20,
+    Bit19 = BasicEnum_ExplicitInt.Bit19,
+    Bit18 = BasicEnum_ExplicitInt.Bit18,
+    Bit17 = BasicEnum_ExplicitInt.Bit17,
+    Bit16 = BasicEnum_ExplicitInt.Bit16,
+    Bit15 = BasicEnum_ExplicitInt.Bit15,
+    Bit14 = BasicEnum_ExplicitInt.Bit14,
+    Bit13 = BasicEnum_ExplicitInt.Bit13,
+    Bit12 = BasicEnum_ExplicitInt.Bit12,
+    Bit11 = BasicEnum_ExplicitInt.Bit11,
+    Bit10 = BasicEnum_ExplicitInt.Bit10,
+    Bit09 = BasicEnum_ExplicitInt.Bit09,
+    Bit08 = BasicEnum_ExplicitInt.Bit08,
+    Bit07 = BasicEnum_ExplicitInt.Bit07,
+    Bit06 = BasicEnum_ExplicitInt.Bit06,
+    Bit05 = BasicEnum_ExplicitInt.Bit05,
+    Bit04 = BasicEnum_ExplicitInt.Bit04,
+    Bit03 = BasicEnum_ExplicitInt.Bit03,
+    Bit02 = BasicEnum_ExplicitInt.Bit02,
+    Bit01 = BasicEnum_ExplicitInt.Bit01,
+    Bit00 = BasicEnum_ExplicitInt.Bit00,
+    All_0 = BasicEnum_ExplicitInt.All_0,
+    All_1 = BasicEnum_ExplicitInt.All_1,
+    Alternating_01 = BasicEnum_ExplicitInt.Alternating_01,
+    Alternating_10 = BasicEnum_ExplicitInt.Alternating_10,
+    EvenBytesHigh = BasicEnum_ExplicitInt.EvenBytesHigh,
+    OddBytesHigh = BasicEnum_ExplicitInt.OddBytesHigh
+}

+ 50 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt_NoFastIsDefined.cs

@@ -0,0 +1,50 @@
+// ReSharper disable EnumUnderlyingTypeIsInt
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="BetterEnum_ExplicitInt"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute.FastIsDefined"/> = <see langword="false" />.
+/// </summary>
+[GenerateEnumExtensionMethods (FastIsDefined = false)]
+public enum BetterEnum_ExplicitInt_NoFastIsDefined : int
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 = 0b_01000000_00000000_00000000_00000000,
+    Bit29 = 0b_00100000_00000000_00000000_00000000,
+    Bit28 = 0b_00010000_00000000_00000000_00000000,
+    Bit27 = 0b_00001000_00000000_00000000_00000000,
+    Bit26 = 0b_00000100_00000000_00000000_00000000,
+    Bit25 = 0b_00000010_00000000_00000000_00000000,
+    Bit24 = 0b_00000001_00000000_00000000_00000000,
+    Bit23 = 0b_00000000_10000000_00000000_00000000,
+    Bit22 = 0b_00000000_01000000_00000000_00000000,
+    Bit21 = 0b_00000000_00100000_00000000_00000000,
+    Bit20 = 0b_00000000_00010000_00000000_00000000,
+    Bit19 = 0b_00000000_00001000_00000000_00000000,
+    Bit18 = 0b_00000000_00000100_00000000_00000000,
+    Bit17 = 0b_00000000_00000010_00000000_00000000,
+    Bit16 = 0b_00000000_00000001_00000000_00000000,
+    Bit15 = 0b_00000000_00000000_10000000_00000000,
+    Bit14 = 0b_00000000_00000000_01000000_00000000,
+    Bit13 = 0b_00000000_00000000_00100000_00000000,
+    Bit12 = 0b_00000000_00000000_00010000_00000000,
+    Bit11 = 0b_00000000_00000000_00001000_00000000,
+    Bit10 = 0b_00000000_00000000_00000100_00000000,
+    Bit09 = 0b_00000000_00000000_00000010_00000000,
+    Bit08 = 0b_00000000_00000000_00000001_00000000,
+    Bit07 = 0b_00000000_00000000_00000000_10000000,
+    Bit06 = 0b_00000000_00000000_00000000_01000000,
+    Bit05 = 0b_00000000_00000000_00000000_00100000,
+    Bit04 = 0b_00000000_00000000_00000000_00010000,
+    Bit03 = 0b_00000000_00000000_00000000_00001000,
+    Bit02 = 0b_00000000_00000000_00000000_00000100,
+    Bit01 = 0b_00000000_00000000_00000000_00000010,
+    Bit00 = 0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 49 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt.cs

@@ -0,0 +1,49 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="BasicEnum_ExplicitUInt"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute"/> applied.
+/// </summary>
+[GenerateEnumExtensionMethods]
+public enum BetterEnum_ExplicitUInt : uint
+{
+    Bit31 = 0b_10000000_00000000_00000000_00000000u,
+    Bit30 = 0b_01000000_00000000_00000000_00000000u,
+    Bit29 = 0b_00100000_00000000_00000000_00000000u,
+    Bit28 = 0b_00010000_00000000_00000000_00000000u,
+    Bit27 = 0b_00001000_00000000_00000000_00000000u,
+    Bit26 = 0b_00000100_00000000_00000000_00000000u,
+    Bit25 = 0b_00000010_00000000_00000000_00000000u,
+    Bit24 = 0b_00000001_00000000_00000000_00000000u,
+    Bit23 = 0b_00000000_10000000_00000000_00000000u,
+    Bit22 = 0b_00000000_01000000_00000000_00000000u,
+    Bit21 = 0b_00000000_00100000_00000000_00000000u,
+    Bit20 = 0b_00000000_00010000_00000000_00000000u,
+    Bit19 = 0b_00000000_00001000_00000000_00000000u,
+    Bit18 = 0b_00000000_00000100_00000000_00000000u,
+    Bit17 = 0b_00000000_00000010_00000000_00000000u,
+    Bit16 = 0b_00000000_00000001_00000000_00000000u,
+    Bit15 = 0b_00000000_00000000_10000000_00000000u,
+    Bit14 = 0b_00000000_00000000_01000000_00000000u,
+    Bit13 = 0b_00000000_00000000_00100000_00000000u,
+    Bit12 = 0b_00000000_00000000_00010000_00000000u,
+    Bit11 = 0b_00000000_00000000_00001000_00000000u,
+    Bit10 = 0b_00000000_00000000_00000100_00000000u,
+    Bit09 = 0b_00000000_00000000_00000010_00000000u,
+    Bit08 = 0b_00000000_00000000_00000001_00000000u,
+    Bit07 = 0b_00000000_00000000_00000000_10000000u,
+    Bit06 = 0b_00000000_00000000_00000000_01000000u,
+    Bit05 = 0b_00000000_00000000_00000000_00100000u,
+    Bit04 = 0b_00000000_00000000_00000000_00010000u,
+    Bit03 = 0b_00000000_00000000_00000000_00001000u,
+    Bit02 = 0b_00000000_00000000_00000000_00000100u,
+    Bit01 = 0b_00000000_00000000_00000000_00000010u,
+    Bit00 = 0b_00000000_00000000_00000000_00000001u,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 49 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt_NoFastIsDefined.cs

@@ -0,0 +1,49 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="BetterEnum_ExplicitUInt"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute.FastIsDefined"/> = <see langword="false" />.
+/// </summary>
+[GenerateEnumExtensionMethods (FastIsDefined = false)]
+public enum BetterEnum_ExplicitUInt_NoFastIsDefined : uint
+{
+    Bit31 = 0b_10000000_00000000_00000000_00000000u,
+    Bit30 = 0b_01000000_00000000_00000000_00000000u,
+    Bit29 = 0b_00100000_00000000_00000000_00000000u,
+    Bit28 = 0b_00010000_00000000_00000000_00000000u,
+    Bit27 = 0b_00001000_00000000_00000000_00000000u,
+    Bit26 = 0b_00000100_00000000_00000000_00000000u,
+    Bit25 = 0b_00000010_00000000_00000000_00000000u,
+    Bit24 = 0b_00000001_00000000_00000000_00000000u,
+    Bit23 = 0b_00000000_10000000_00000000_00000000u,
+    Bit22 = 0b_00000000_01000000_00000000_00000000u,
+    Bit21 = 0b_00000000_00100000_00000000_00000000u,
+    Bit20 = 0b_00000000_00010000_00000000_00000000u,
+    Bit19 = 0b_00000000_00001000_00000000_00000000u,
+    Bit18 = 0b_00000000_00000100_00000000_00000000u,
+    Bit17 = 0b_00000000_00000010_00000000_00000000u,
+    Bit16 = 0b_00000000_00000001_00000000_00000000u,
+    Bit15 = 0b_00000000_00000000_10000000_00000000u,
+    Bit14 = 0b_00000000_00000000_01000000_00000000u,
+    Bit13 = 0b_00000000_00000000_00100000_00000000u,
+    Bit12 = 0b_00000000_00000000_00010000_00000000u,
+    Bit11 = 0b_00000000_00000000_00001000_00000000u,
+    Bit10 = 0b_00000000_00000000_00000100_00000000u,
+    Bit09 = 0b_00000000_00000000_00000010_00000000u,
+    Bit08 = 0b_00000000_00000000_00000001_00000000u,
+    Bit07 = 0b_00000000_00000000_00000000_10000000u,
+    Bit06 = 0b_00000000_00000000_00000000_01000000u,
+    Bit05 = 0b_00000000_00000000_00000000_00100000u,
+    Bit04 = 0b_00000000_00000000_00000000_00010000u,
+    Bit03 = 0b_00000000_00000000_00000000_00001000u,
+    Bit02 = 0b_00000000_00000000_00000000_00000100u,
+    Bit01 = 0b_00000000_00000000_00000000_00000010u,
+    Bit00 = 0b_00000000_00000000_00000000_00000001u,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 49 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_NoFastIsDefined.cs

@@ -0,0 +1,49 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="BetterEnum"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute.FastIsDefined"/> = <see langword="false" />.
+/// </summary>
+[GenerateEnumExtensionMethods (FastIsDefined = false)]
+public enum BetterEnum_NoFastIsDefined
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 =  0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 =  0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 =  0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 50 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum.cs

@@ -0,0 +1,50 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="FlagsEnum"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute"/> applied.
+/// </summary>
+[Flags]
+[GenerateEnumExtensionMethods]
+public enum BetterFlagsEnum
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 = -0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 = -0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 = -0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 51 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitInt.cs

@@ -0,0 +1,51 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+/// <summary>
+///     Same as <see cref="FlagsEnum_ExplicitInt"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute"/> applied.
+/// </summary>
+[Flags]
+[GenerateEnumExtensionMethods]
+public enum BetterFlagsEnum_ExplicitInt : int
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 = -0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 = -0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 = -0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 50 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitUInt.cs

@@ -0,0 +1,50 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Same as <see cref="FlagsEnum_ExplicitUInt"/>, but with <see cref="GenerateEnumExtensionMethodsAttribute"/> applied.
+/// </summary>
+[Flags]
+[GenerateEnumExtensionMethods]
+public enum BetterFlagsEnum_ExplicitUInt : uint
+{
+    Bit31 = 0b_10000000_00000000_00000000_00000000u,
+    Bit30 = 0b_01000000_00000000_00000000_00000000u,
+    Bit29 = 0b_00100000_00000000_00000000_00000000u,
+    Bit28 = 0b_00010000_00000000_00000000_00000000u,
+    Bit27 = 0b_00001000_00000000_00000000_00000000u,
+    Bit26 = 0b_00000100_00000000_00000000_00000000u,
+    Bit25 = 0b_00000010_00000000_00000000_00000000u,
+    Bit24 = 0b_00000001_00000000_00000000_00000000u,
+    Bit23 = 0b_00000000_10000000_00000000_00000000u,
+    Bit22 = 0b_00000000_01000000_00000000_00000000u,
+    Bit21 = 0b_00000000_00100000_00000000_00000000u,
+    Bit20 = 0b_00000000_00010000_00000000_00000000u,
+    Bit19 = 0b_00000000_00001000_00000000_00000000u,
+    Bit18 = 0b_00000000_00000100_00000000_00000000u,
+    Bit17 = 0b_00000000_00000010_00000000_00000000u,
+    Bit16 = 0b_00000000_00000001_00000000_00000000u,
+    Bit15 = 0b_00000000_00000000_10000000_00000000u,
+    Bit14 = 0b_00000000_00000000_01000000_00000000u,
+    Bit13 = 0b_00000000_00000000_00100000_00000000u,
+    Bit12 = 0b_00000000_00000000_00010000_00000000u,
+    Bit11 = 0b_00000000_00000000_00001000_00000000u,
+    Bit10 = 0b_00000000_00000000_00000100_00000000u,
+    Bit09 = 0b_00000000_00000000_00000010_00000000u,
+    Bit08 = 0b_00000000_00000000_00000001_00000000u,
+    Bit07 = 0b_00000000_00000000_00000000_10000000u,
+    Bit06 = 0b_00000000_00000000_00000000_01000000u,
+    Bit05 = 0b_00000000_00000000_00000000_00100000u,
+    Bit04 = 0b_00000000_00000000_00000000_00010000u,
+    Bit03 = 0b_00000000_00000000_00000000_00001000u,
+    Bit02 = 0b_00000000_00000000_00000000_00000100u,
+    Bit01 = 0b_00000000_00000000_00000000_00000010u,
+    Bit00 = 0b_00000000_00000000_00000000_00000001u,
+    All_0  =  0,
+    All_1  = ~All_0,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = ~Alternating_01,
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+    OddBytesHigh = ~EvenBytesHigh,
+}

+ 46 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum.cs

@@ -0,0 +1,46 @@
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Basic enum without explicitly-defined backing type and no attributes on the enum or any of its members.
+/// </summary>
+public enum BasicEnum
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 =  0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 =  0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 =  0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = -1,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = unchecked((int)0b_10101010_10101010_10101010_10101010),
+    OddBytesHigh = unchecked((int)0b_11111111_00000000_11111111_00000000),
+    EvenBytesHigh = 0b_00000000_11111111_00000000_11111111,
+}

+ 48 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitInt.cs

@@ -0,0 +1,48 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Basic enum with explicitly-defined backing type of int and no attributes on the enum or any of its members.
+/// </summary>
+public enum BasicEnum_ExplicitInt : int
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 =  0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 =  0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 =  0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = -1,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101,
+    Alternating_10 = unchecked((int)0b_10101010_10101010_10101010_10101010),
+    OddBytesHigh = unchecked((int)0b_11111111_00000000_11111111_00000000),
+    EvenBytesHigh = unchecked((int)0b_00000000_11111111_00000000_11111111),
+}

+ 46 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitUint.cs

@@ -0,0 +1,46 @@
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Basic enum with explicitly-defined backing type of uint and no attributes on the enum or any of its members.
+/// </summary>
+public enum BasicEnum_ExplicitUInt : uint
+{
+    Bit31 = 0b_10000000_00000000_00000000_00000000u,
+    Bit30 = 0b_01000000_00000000_00000000_00000000u,
+    Bit29 = 0b_00100000_00000000_00000000_00000000u,
+    Bit28 = 0b_00010000_00000000_00000000_00000000u,
+    Bit27 = 0b_00001000_00000000_00000000_00000000u,
+    Bit26 = 0b_00000100_00000000_00000000_00000000u,
+    Bit25 = 0b_00000010_00000000_00000000_00000000u,
+    Bit24 = 0b_00000001_00000000_00000000_00000000u,
+    Bit23 = 0b_00000000_10000000_00000000_00000000u,
+    Bit22 = 0b_00000000_01000000_00000000_00000000u,
+    Bit21 = 0b_00000000_00100000_00000000_00000000u,
+    Bit20 = 0b_00000000_00010000_00000000_00000000u,
+    Bit19 = 0b_00000000_00001000_00000000_00000000u,
+    Bit18 = 0b_00000000_00000100_00000000_00000000u,
+    Bit17 = 0b_00000000_00000010_00000000_00000000u,
+    Bit16 = 0b_00000000_00000001_00000000_00000000u,
+    Bit15 = 0b_00000000_00000000_10000000_00000000u,
+    Bit14 = 0b_00000000_00000000_01000000_00000000u,
+    Bit13 = 0b_00000000_00000000_00100000_00000000u,
+    Bit12 = 0b_00000000_00000000_00010000_00000000u,
+    Bit11 = 0b_00000000_00000000_00001000_00000000u,
+    Bit10 = 0b_00000000_00000000_00000100_00000000u,
+    Bit09 = 0b_00000000_00000000_00000010_00000000u,
+    Bit08 = 0b_00000000_00000000_00000001_00000000u,
+    Bit07 = 0b_00000000_00000000_00000000_10000000u,
+    Bit06 = 0b_00000000_00000000_00000000_01000000u,
+    Bit05 = 0b_00000000_00000000_00000000_00100000u,
+    Bit04 = 0b_00000000_00000000_00000000_00010000u,
+    Bit03 = 0b_00000000_00000000_00000000_00001000u,
+    Bit02 = 0b_00000000_00000000_00000000_00000100u,
+    Bit01 = 0b_00000000_00000000_00000000_00000010u,
+    Bit00 = 0b_00000000_00000000_00000000_00000001u,
+    All_0 = 0b_00000000_00000000_00000000_00000000u,
+    All_1 = 0b_11111111_11111111_11111111_11111111u,
+    Alternating_01 = 0b_01010101_01010101_01010101_01010101u,
+    Alternating_10 = 0b_10101010_10101010_10101010_10101010u,
+    OddBytesHigh   = 0b_11111111_00000000_11111111_00000000u,
+    EvenBytesHigh  = 0b_00000000_11111111_00000000_11111111u,
+}

+ 43 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum.cs

@@ -0,0 +1,43 @@
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Flags enum without explicitly-defined backing type and only a <see cref="FlagsAttribute"/> on the enum declaration No other attributes on the enum or its members..
+/// </summary>
+[Flags]
+public enum FlagsEnum
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 = -0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 = -0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 = -0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = -1
+}

+ 43 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitInt.cs

@@ -0,0 +1,43 @@
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Flags enum with explicitly-defined backing type of int and only a <see cref="FlagsAttribute"/> on the enum declaration No other attributes on the enum or its members..
+/// </summary>
+[Flags]
+public enum FlagsEnum_ExplicitInt : int
+{
+    Bit31 = -0b_10000000_00000000_00000000_00000000,
+    Bit30 =  0b_01000000_00000000_00000000_00000000,
+    Bit29 =  0b_00100000_00000000_00000000_00000000,
+    Bit28 =  0b_00010000_00000000_00000000_00000000,
+    Bit27 =  0b_00001000_00000000_00000000_00000000,
+    Bit26 =  0b_00000100_00000000_00000000_00000000,
+    Bit25 =  0b_00000010_00000000_00000000_00000000,
+    Bit24 =  0b_00000001_00000000_00000000_00000000,
+    Bit23 = -0b_00000000_10000000_00000000_00000000,
+    Bit22 =  0b_00000000_01000000_00000000_00000000,
+    Bit21 =  0b_00000000_00100000_00000000_00000000,
+    Bit20 =  0b_00000000_00010000_00000000_00000000,
+    Bit19 =  0b_00000000_00001000_00000000_00000000,
+    Bit18 =  0b_00000000_00000100_00000000_00000000,
+    Bit17 =  0b_00000000_00000010_00000000_00000000,
+    Bit16 =  0b_00000000_00000001_00000000_00000000,
+    Bit15 = -0b_00000000_00000000_10000000_00000000,
+    Bit14 =  0b_00000000_00000000_01000000_00000000,
+    Bit13 =  0b_00000000_00000000_00100000_00000000,
+    Bit12 =  0b_00000000_00000000_00010000_00000000,
+    Bit11 =  0b_00000000_00000000_00001000_00000000,
+    Bit10 =  0b_00000000_00000000_00000100_00000000,
+    Bit09 =  0b_00000000_00000000_00000010_00000000,
+    Bit08 =  0b_00000000_00000000_00000001_00000000,
+    Bit07 = -0b_00000000_00000000_00000000_10000000,
+    Bit06 =  0b_00000000_00000000_00000000_01000000,
+    Bit05 =  0b_00000000_00000000_00000000_00100000,
+    Bit04 =  0b_00000000_00000000_00000000_00010000,
+    Bit03 =  0b_00000000_00000000_00000000_00001000,
+    Bit02 =  0b_00000000_00000000_00000000_00000100,
+    Bit01 =  0b_00000000_00000000_00000000_00000010,
+    Bit00 =  0b_00000000_00000000_00000000_00000001,
+    All_0  =  0,
+    All_1  = -1
+}

+ 43 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitUInt.cs

@@ -0,0 +1,43 @@
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions;
+
+/// <summary>
+///     Flags enum with explicitly-defined backing type of uint and only a <see cref="FlagsAttribute"/> on the enum declaration No other attributes on the enum or its members..
+/// </summary>
+[Flags]
+public enum FlagsEnum_ExplicitUInt : uint
+{
+    Bit31 = 0b_10000000_00000000_00000000_00000000u,
+    Bit30 = 0b_01000000_00000000_00000000_00000000u,
+    Bit29 = 0b_00100000_00000000_00000000_00000000u,
+    Bit28 = 0b_00010000_00000000_00000000_00000000u,
+    Bit27 = 0b_00001000_00000000_00000000_00000000u,
+    Bit26 = 0b_00000100_00000000_00000000_00000000u,
+    Bit25 = 0b_00000010_00000000_00000000_00000000u,
+    Bit24 = 0b_00000001_00000000_00000000_00000000u,
+    Bit23 = 0b_00000000_10000000_00000000_00000000u,
+    Bit22 = 0b_00000000_01000000_00000000_00000000u,
+    Bit21 = 0b_00000000_00100000_00000000_00000000u,
+    Bit20 = 0b_00000000_00010000_00000000_00000000u,
+    Bit19 = 0b_00000000_00001000_00000000_00000000u,
+    Bit18 = 0b_00000000_00000100_00000000_00000000u,
+    Bit17 = 0b_00000000_00000010_00000000_00000000u,
+    Bit16 = 0b_00000000_00000001_00000000_00000000u,
+    Bit15 = 0b_00000000_00000000_10000000_00000000u,
+    Bit14 = 0b_00000000_00000000_01000000_00000000u,
+    Bit13 = 0b_00000000_00000000_00100000_00000000u,
+    Bit12 = 0b_00000000_00000000_00010000_00000000u,
+    Bit11 = 0b_00000000_00000000_00001000_00000000u,
+    Bit10 = 0b_00000000_00000000_00000100_00000000u,
+    Bit09 = 0b_00000000_00000000_00000010_00000000u,
+    Bit08 = 0b_00000000_00000000_00000001_00000000u,
+    Bit07 = 0b_00000000_00000000_00000000_10000000u,
+    Bit06 = 0b_00000000_00000000_00000000_01000000u,
+    Bit05 = 0b_00000000_00000000_00000000_00100000u,
+    Bit04 = 0b_00000000_00000000_00000000_00010000u,
+    Bit03 = 0b_00000000_00000000_00000000_00001000u,
+    Bit02 = 0b_00000000_00000000_00000000_00000100u,
+    Bit01 = 0b_00000000_00000000_00000000_00000010u,
+    Bit00 = 0b_00000000_00000000_00000000_00000001u,
+    All_0 = 0b_00000000_00000000_00000000_00000000u,
+    All_1 = 0b_11111111_11111111_11111111_11111111u
+}

+ 329 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGeneratorTests.cs

@@ -0,0 +1,329 @@
+using System.Collections.Concurrent;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using NUnit.Framework.Interfaces;
+using NUnit.Framework.Internal;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions;
+
+[TestFixture]
+[Category ("Source Generators")]
+[TestOf (typeof (EnumExtensionMethodsIncrementalGenerator))]
+[Parallelizable (ParallelScope.Children)]
+public class EnumExtensionMethodsIncrementalGeneratorTests
+{
+    private static bool _isInitialized;
+
+    /// <summary>All enum types declared in the test assembly.</summary>
+    private static readonly ObservableCollection<Type> AllEnumTypes = [];
+
+    /// <summary>
+    ///     All enum types without a <see cref="GenerateEnumExtensionMethodsAttribute"/>, <see cref="AllEnumTypes"/>
+    /// </summary>
+    private static readonly HashSet<Type> BoringEnumTypes = [];
+
+    /// <summary>All extension classes generated for enums with our attribute.</summary>
+    private static readonly ObservableCollection<Type> EnumExtensionClasses = [];
+
+    private static readonly ConcurrentDictionary<Type, EnumData> ExtendedEnumTypeMappings = [];
+    private static IEnumerable<Type> ExtendedEnumTypes => ExtendedEnumTypeMappings.Keys;
+
+    private static readonly ReaderWriterLockSlim InitializationLock = new ();
+
+    private static IEnumerable<AssemblyExtendedEnumTypeAttribute> GetAssemblyExtendedEnumTypeAttributes () =>
+        Assembly.GetExecutingAssembly ()
+                .GetCustomAttributes<AssemblyExtendedEnumTypeAttribute> ();
+
+    private static IEnumerable<TestCaseData> Get_AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute_Cases ()
+    {
+        return GetAssemblyExtendedEnumTypeAttributes ()
+            .Select (
+                     static attr => new TestCaseData (attr)
+                     {
+                         TestName = $"{nameof (AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute)}({attr.EnumType.Name},{attr.ExtensionClass.Name})",
+                         HasExpectedResult = true,
+                         ExpectedResult = true
+                     });
+    }
+
+    [Test]
+    [Category ("Attributes")]
+    [TestCaseSource (nameof (Get_AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute_Cases))]
+    public bool AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute (AssemblyExtendedEnumTypeAttribute attr)
+    {
+        Assume.That (attr, Is.Not.Null);
+        Assume.That (attr.EnumType, Is.Not.Null);
+        Assume.That (attr.EnumType!.IsEnum);
+
+        return attr.EnumType.IsDefined (typeof (GenerateEnumExtensionMethodsAttribute));
+    }
+
+    private const string AssemblyExtendedEnumTypeAttributeEnumPropertyName =
+        $"{nameof (AssemblyExtendedEnumTypeAttribute)}.{nameof (AssemblyExtendedEnumTypeAttribute.EnumType)}";
+
+    [Test]
+    [Category("Attributes")]
+    public void AssemblyExtendedEnumTypeAttribute_ExtensionClassHasExpectedReverseMappingAttribute ([ValueSource(nameof(GetAssemblyExtendedEnumTypeAttributes))]AssemblyExtendedEnumTypeAttribute attr)
+    {
+        Assume.That (attr, Is.Not.Null);
+        Assume.That (attr.ExtensionClass, Is.Not.Null);
+        Assume.That (attr.ExtensionClass!.IsClass);
+        Assume.That (attr.ExtensionClass!.IsSealed);
+
+        Assert.That (attr.ExtensionClass.IsDefined (typeof (ExtensionsForEnumTypeAttribute<>)));
+    }
+
+    [Test]
+    [Category("Attributes")]
+    public void ExtendedEnum_AssemblyHasMatchingAttribute ([ValueSource(nameof(GetExtendedEnum_EnumData))]EnumData enumData)
+    {
+        Assume.That (enumData, Is.Not.Null);
+        Assume.That (enumData.EnumType, Is.Not.Null);
+        Assume.That (enumData.EnumType!.IsEnum);
+
+        Assert.That (enumData.EnumType, Has.Attribute<GenerateEnumExtensionMethodsAttribute> ());
+    }
+
+    [Test]
+    public void BoringEnum_DoesNotHaveExtensions ([ValueSource (nameof (BoringEnumTypes))] Type enumType)
+    {
+        Assume.That (enumType.IsEnum);
+
+        Assert.That (enumType, Has.No.Attribute<GenerateEnumExtensionMethodsAttribute> ());
+    }
+
+    [Test]
+    public void ExtendedEnum_FastIsDefinedFalse_DoesNotHaveFastIsDefined ([ValueSource (nameof (GetExtendedEnumTypes_FastIsDefinedFalse))] EnumData enumData)
+    {
+        Assume.That (enumData.EnumType.IsEnum);
+        Assume.That (enumData.EnumType, Has.Attribute<GenerateEnumExtensionMethodsAttribute> ());
+        Assume.That (enumData.GeneratorAttribute, Is.Not.Null);
+        Assume.That (enumData.GeneratorAttribute, Is.EqualTo (enumData.EnumType.GetCustomAttribute<GenerateEnumExtensionMethodsAttribute> ()));
+        Assume.That (enumData.GeneratorAttribute, Has.Property ("FastIsDefined").False);
+        Assume.That (enumData.ExtensionClass, Is.Not.Null);
+
+        Assert.That (enumData.ExtensionClass!.GetMethod ("FastIsDefined"), Is.Null);
+    }
+
+    [Test]
+    public void ExtendedEnum_StaticExtensionClassExists ([ValueSource (nameof (ExtendedEnumTypes))] Type enumType)
+    {
+        Assume.That (enumType.IsEnum);
+        Assume.That (enumType, Has.Attribute<GenerateEnumExtensionMethodsAttribute> ());
+        ITypeInfo enumTypeInfo = new TypeWrapper (enumType);
+        Assume.That (enumType, Has.Attribute<GenerateEnumExtensionMethodsAttribute> ());
+    }
+
+    [Test]
+    public void ExtendedEnum_FastIsDefinedTrue_HasFastIsDefined ([ValueSource (nameof (GetExtendedEnumTypes_FastIsDefinedTrue))] EnumData enumData)
+    {
+        Assume.That (enumData.EnumType, Is.Not.Null);
+        Assume.That (enumData.EnumType.IsEnum);
+        Assume.That (enumData.EnumType, Has.Attribute<GenerateEnumExtensionMethodsAttribute> ());
+        Assume.That (enumData.ExtensionClass, Is.Not.Null);
+        ITypeInfo extensionClassTypeInfo = new TypeWrapper (enumData.ExtensionClass!);
+        Assume.That (extensionClassTypeInfo.IsStaticClass);
+        Assume.That (enumData.GeneratorAttribute, Is.Not.Null);
+        Assume.That (enumData.GeneratorAttribute, Is.EqualTo (enumData.EnumType.GetCustomAttribute<GenerateEnumExtensionMethodsAttribute> ()));
+        Assume.That (enumData.GeneratorAttribute, Has.Property ("FastIsDefined").True);
+
+        MethodInfo? fastIsDefinedMethod = enumData.ExtensionClass!.GetMethod ("FastIsDefined");
+
+        Assert.That (fastIsDefinedMethod, Is.Not.Null);
+        Assert.That (fastIsDefinedMethod, Has.Attribute<ExtensionAttribute> ());
+        IMethodInfo[] extensionMethods = extensionClassTypeInfo.GetMethodsWithAttribute<ExtensionAttribute> (false);
+
+
+    }
+
+    private static IEnumerable<EnumData> GetExtendedEnum_EnumData ()
+    {
+        InitializationLock.EnterUpgradeableReadLock ();
+
+        try
+        {
+            if (!_isInitialized)
+            {
+                Initialize ();
+            }
+
+            return ExtendedEnumTypeMappings.Values;
+        }
+        finally
+        {
+            InitializationLock.ExitUpgradeableReadLock ();
+        }
+    }
+
+    private static IEnumerable<Type> GetBoringEnumTypes ()
+    {
+        InitializationLock.EnterUpgradeableReadLock ();
+
+        try
+        {
+            if (!_isInitialized)
+            {
+                Initialize ();
+            }
+
+            return BoringEnumTypes;
+        }
+        finally
+        {
+            InitializationLock.ExitUpgradeableReadLock ();
+        }
+    }
+
+    private static IEnumerable<EnumData> GetExtendedEnumTypes_FastIsDefinedFalse ()
+    {
+        InitializationLock.EnterUpgradeableReadLock ();
+
+        try
+        {
+            if (!_isInitialized)
+            {
+                Initialize ();
+            }
+
+            return ExtendedEnumTypeMappings.Values.Where (static t => t.GeneratorAttribute?.FastIsDefined is false);
+        }
+        finally
+        {
+            InitializationLock.ExitUpgradeableReadLock ();
+        }
+    }
+
+    private static IEnumerable<EnumData> GetExtendedEnumTypes_FastIsDefinedTrue ()
+    {
+        InitializationLock.EnterUpgradeableReadLock ();
+
+        try
+        {
+            if (!_isInitialized)
+            {
+                Initialize ();
+            }
+
+            return ExtendedEnumTypeMappings.Values.Where (static t => t.GeneratorAttribute?.FastIsDefined is true);
+        }
+        finally
+        {
+            InitializationLock.ExitUpgradeableReadLock ();
+        }
+    }
+
+    private static void Initialize ()
+    {
+        if (!InitializationLock.IsUpgradeableReadLockHeld || !InitializationLock.TryEnterWriteLock (5000))
+        {
+            return;
+        }
+
+        try
+        {
+            if (_isInitialized)
+            {
+                return;
+            }
+
+            AllEnumTypes.CollectionChanged += AllEnumTypes_CollectionChanged;
+            EnumExtensionClasses.CollectionChanged += EnumExtensionClasses_OnCollectionChanged;
+
+            Type [] allAssemblyTypes = Assembly
+                                       .GetExecutingAssembly ()
+                                       .GetTypes ();
+
+            IEnumerable<Type> allEnumTypes = allAssemblyTypes.Where (IsDefinedEnum);
+
+            foreach (Type type in allEnumTypes)
+            {
+                AllEnumTypes.Add (type);
+            }
+
+            foreach (Type type in allAssemblyTypes.Where (static t => t.IsClass && t.IsDefined (typeof (ExtensionsForEnumTypeAttribute<>))))
+            {
+                EnumExtensionClasses.Add (type);
+            }
+
+            _isInitialized = true;
+        }
+        finally
+        {
+            InitializationLock.ExitWriteLock ();
+        }
+
+        return;
+
+        static bool IsDefinedEnum (Type t) { return t is { IsEnum: true, IsGenericType: false, IsConstructedGenericType: false, IsTypeDefinition: true }; }
+
+        static void AllEnumTypes_CollectionChanged (object? sender, NotifyCollectionChangedEventArgs e)
+        {
+            if (e.Action is not NotifyCollectionChangedAction.Add and not NotifyCollectionChangedAction.Replace || e.NewItems is null)
+            {
+                return;
+            }
+
+            foreach (Type enumType in e.NewItems.OfType<Type> ())
+            {
+                if (enumType.GetCustomAttribute<GenerateEnumExtensionMethodsAttribute> () is not { } generatorAttribute)
+                {
+                    BoringEnumTypes.Add (enumType);
+
+                    continue;
+                }
+
+                ExtendedEnumTypeMappings.AddOrUpdate (
+                                               enumType,
+                                               CreateNewEnumData,
+                                               UpdateGeneratorAttributeProperty,
+                                               generatorAttribute);
+            }
+        }
+
+        static EnumData CreateNewEnumData (Type tEnum, GenerateEnumExtensionMethodsAttribute attr) { return new (tEnum, attr); }
+
+        static EnumData UpdateGeneratorAttributeProperty (Type tEnum, EnumData data, GenerateEnumExtensionMethodsAttribute attr)
+        {
+            data.GeneratorAttribute ??= attr;
+
+            return data;
+        }
+
+        static void EnumExtensionClasses_OnCollectionChanged (object? sender, NotifyCollectionChangedEventArgs e)
+        {
+            if (e.Action != NotifyCollectionChangedAction.Add)
+            {
+                return;
+            }
+
+            foreach (Type extensionClassType in e.NewItems!.OfType<Type> ())
+            {
+                if (extensionClassType.GetCustomAttribute (typeof (ExtensionsForEnumTypeAttribute<>), false) is not IExtensionsForEnumTypeAttributes
+                        {
+                            EnumType.IsEnum: true
+                        } extensionForAttribute)
+                {
+                    continue;
+                }
+
+                ExtendedEnumTypeMappings [extensionForAttribute.EnumType].ExtensionClass ??= extensionClassType;
+            }
+        }
+    }
+
+    public sealed record EnumData (
+        Type EnumType,
+        GenerateEnumExtensionMethodsAttribute? GeneratorAttribute = null,
+        Type? ExtensionClass = null,
+        IExtensionsForEnumTypeAttributes? ExtensionForEnumTypeAttribute = null)
+    {
+        public Type? ExtensionClass { get; set; } = ExtensionClass;
+
+        public IExtensionsForEnumTypeAttributes? ExtensionForEnumTypeAttribute { get; set; } = ExtensionForEnumTypeAttribute;
+        public GenerateEnumExtensionMethodsAttribute? GeneratorAttribute { get; set; } = GeneratorAttribute;
+    }
+}

+ 111 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/IndentedTextWriterExtensionsTests.cs

@@ -0,0 +1,111 @@
+using System.CodeDom.Compiler;
+using System.Text;
+
+namespace Terminal.Gui.Analyzers.Internal.Tests;
+
+[TestFixture]
+[Category ("Extension Methods")]
+[TestOf (typeof (IndentedTextWriterExtensions))]
+[Parallelizable (ParallelScope.Children)]
+public class IndentedTextWriterExtensionsTests
+{
+    [Test]
+    public void Pop_Decrements ()
+    {
+        StringBuilder sb = new (0);
+        using var sw = new StringWriter (sb);
+        using var writer = new IndentedTextWriter (sw);
+        writer.Indent = 5;
+
+        Assume.That (writer.Indent, Is.EqualTo (5));
+
+        writer.Pop ();
+        Assert.That (writer.Indent, Is.EqualTo (4));
+    }
+
+    [Test]
+    public void Pop_WithClosing_WritesAndPops ([Values ("}", ")", "]")] string scopeClosing)
+    {
+        StringBuilder sb = new (256);
+        using var sw = new StringWriter (sb);
+        using var writer = new IndentedTextWriter (sw, "  ");
+        writer.Indent = 5;
+        writer.Flush ();
+        Assume.That (writer.Indent, Is.EqualTo (5));
+        Assume.That (sb.Length, Is.Zero);
+
+        // Need to write something first, or IndentedTextWriter won't emit the indentation for the first call.
+        // So we'll write an empty line.
+        writer.WriteLine ();
+
+        for (ushort indentCount = 5; indentCount > 0;)
+        {
+            writer.Pop (scopeClosing);
+            Assert.That (writer.Indent, Is.EqualTo (--indentCount));
+        }
+
+        writer.Flush ();
+        var result = sb.ToString ();
+
+        Assert.That (
+                     result,
+                     Is.EqualTo (
+                                 $"""
+                                  
+                                          {scopeClosing}
+                                        {scopeClosing}
+                                      {scopeClosing}
+                                    {scopeClosing}
+                                  {scopeClosing}
+
+                                  """));
+    }
+
+    [Test]
+    public void Push_Increments ()
+    {
+        StringBuilder sb = new (32);
+        using var sw = new StringWriter (sb);
+        using var writer = new IndentedTextWriter (sw, "  ");
+
+        for (int indentCount = 0; indentCount < 5; indentCount++)
+        {
+            writer.Push ();
+            Assert.That (writer.Indent, Is.EqualTo (indentCount + 1));
+        }
+    }
+
+    [Test]
+    public void Push_WithOpening_WritesAndPushes ([Values ('{', '(', '[')] char scopeOpening)
+    {
+        StringBuilder sb = new (256);
+        using var sw = new StringWriter (sb);
+        using var writer = new IndentedTextWriter (sw, "  ");
+
+        for (ushort indentCount = 0; indentCount < 5;)
+        {
+            writer.Push ("Opening UninterestingEnum", scopeOpening);
+            Assert.That (writer.Indent, Is.EqualTo (++indentCount));
+        }
+
+        writer.Flush ();
+        var result = sb.ToString ();
+
+        Assert.That (
+                     result,
+                     Is.EqualTo (
+                                 $"""
+                                  Opening UninterestingEnum
+                                  {scopeOpening}
+                                    Opening UninterestingEnum
+                                    {scopeOpening}
+                                      Opening UninterestingEnum
+                                      {scopeOpening}
+                                        Opening UninterestingEnum
+                                        {scopeOpening}
+                                          Opening UninterestingEnum
+                                          {scopeOpening}
+
+                                  """));
+    }
+}

+ 50 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj

@@ -0,0 +1,50 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <LangVersion>12</LangVersion>
+    <Nullable>enable</Nullable>
+    <IsPackable>false</IsPackable>
+    <IsTestProject>true</IsTestProject>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineTrace>True</DefineTrace>
+    <DebugType>portable</DebugType>
+    <DefineConstants>$(DefineConstants);JETBRAINS_ANNOTATIONS;CONTRACTS_FULL;CODE_ANALYSIS</DefineConstants>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <NoLogo>True</NoLogo>
+    <SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.9.2" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
+    <PackageReference Include="NUnit" Version="4.1.0" />
+    <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
+    <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Terminal.Gui.Analyzers.Internal\Terminal.Gui.Analyzers.Internal.csproj">
+      <PrivateAssets>all</PrivateAssets>
+      <OutputItemType>Analyzer</OutputItemType>
+      <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+    </ProjectReference>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Using Include="NUnit.Framework" />
+    <Using Include="Terminal.Gui" />
+    <Using Include="Terminal.Gui.Analyzers" />
+    <Using Include="Terminal.Gui.Analyzers.Internal" />
+  </ItemGroup>
+
+</Project>

+ 3 - 0
Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj.DotSettings

@@ -0,0 +1,3 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generators_005Cenumextensions_005Cenumdefinitions_005Cwithgenerator/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generators_005Cenumextensions_005Cenumdefinitions_005Cwithoutgenerator/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 20 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/AccessibilityExtensions.cs

@@ -0,0 +1,20 @@
+using Microsoft.CodeAnalysis;
+
+namespace Terminal.Gui.Analyzers.Internal;
+
+internal static class AccessibilityExtensions
+{
+    internal static string ToCSharpString (this Accessibility value)
+    {
+        return value switch
+        {
+            Accessibility.Public => "public",
+            Accessibility.Internal => "internal",
+            Accessibility.Private => "private",
+            Accessibility.Protected => "protected",
+            Accessibility.ProtectedAndInternal => "private protected",
+            Accessibility.ProtectedOrInternal => "protected internal",
+            _ => string.Empty
+        };
+    }
+}

+ 8 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Shipped.md

@@ -0,0 +1,8 @@
+## Release 1.0
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+TG0001  |   Usage  |  Error   | TG0001_GlobalNamespaceNotSupported
+TG0002  |   Usage  |  Error   | TG0002_UnderlyingTypeNotSupported

+ 4 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Unshipped.md

@@ -0,0 +1,4 @@
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------

+ 117 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs

@@ -0,0 +1,117 @@
+#define JETBRAINS_ANNOTATIONS
+using System.Collections.Immutable;
+using System.Linq;
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+namespace Terminal.Gui.Analyzers.Internal.Analyzers;
+
+/// <summary>
+///     Design-time analyzer that checks for proper use of <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+/// </summary>
+[DiagnosticAnalyzer (LanguageNames.CSharp)]
+[UsedImplicitly]
+internal sealed class GenerateEnumExtensionMethodsAttributeAnalyzer : DiagnosticAnalyzer
+{
+    // ReSharper disable once InconsistentNaming
+    private static readonly DiagnosticDescriptor TG0001_GlobalNamespaceNotSupported = new (
+                                                                                           // ReSharper restore InconsistentNaming
+                                                                                           "TG0001",
+                                                                                           $"{nameof (GenerateEnumExtensionMethodsAttribute)} not supported on global enums",
+                                                                                           "{0} is in the global namespace, which is not supported by the source generator ({1}) used by {2}. Move the enum to a namespace or remove the attribute.",
+                                                                                           "Usage",
+                                                                                           DiagnosticSeverity.Error,
+                                                                                           true,
+                                                                                           null,
+                                                                                           null,
+                                                                                           WellKnownDiagnosticTags.NotConfigurable,
+                                                                                           WellKnownDiagnosticTags.Compiler);
+
+    // ReSharper disable once InconsistentNaming
+    private static readonly DiagnosticDescriptor TG0002_UnderlyingTypeNotSupported = new (
+                                                                                          "TG0002",
+                                                                                          $"{nameof (GenerateEnumExtensionMethodsAttribute)} not supported for this enum type",
+                                                                                          "{0} has an underlying type of {1}, which is not supported by the source generator ({2}) used by {3}. Only enums backed by int or uint are supported.",
+                                                                                          "Usage",
+                                                                                          DiagnosticSeverity.Error,
+                                                                                          true,
+                                                                                          null,
+                                                                                          null,
+                                                                                          WellKnownDiagnosticTags.NotConfigurable,
+                                                                                          WellKnownDiagnosticTags.Compiler);
+
+    /// <inheritdoc/>
+    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
+        [
+            TG0001_GlobalNamespaceNotSupported,
+            TG0002_UnderlyingTypeNotSupported
+        ];
+
+    /// <inheritdoc/>
+    public override void Initialize (AnalysisContext context)
+    {
+        context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.None);
+        context.EnableConcurrentExecution ();
+
+        context.RegisterSyntaxNodeAction (CheckAttributeLocations, SyntaxKind.EnumDeclaration);
+
+        return;
+
+        static void CheckAttributeLocations (SyntaxNodeAnalysisContext analysisContext)
+        {
+            ISymbol? symbol = analysisContext.SemanticModel.GetDeclaredSymbol (analysisContext.Node) as INamedTypeSymbol;
+
+            if (symbol is not INamedTypeSymbol { EnumUnderlyingType: { } } enumSymbol)
+            {
+                // Somehow not even an enum declaration.
+                // Skip it.
+                return;
+            }
+
+            // Check attributes for those we care about and react accordingly.
+            foreach (AttributeData attributeData in enumSymbol.GetAttributes ())
+            {
+                if (attributeData.AttributeClass?.Name != nameof (GenerateEnumExtensionMethodsAttribute))
+                {
+                    // Just skip - not an interesting attribute.
+                    continue;
+                }
+
+                // Check enum underlying type for supported types (int and uint, currently)
+                // Report TG0002 if unsupported underlying type.
+                if (enumSymbol.EnumUnderlyingType is not { SpecialType: SpecialType.System_Int32 or SpecialType.System_UInt32 })
+                {
+                    analysisContext.ReportDiagnostic (
+                                                      Diagnostic.Create (
+                                                                         TG0002_UnderlyingTypeNotSupported,
+                                                                         enumSymbol.Locations.FirstOrDefault (),
+                                                                         enumSymbol.Name,
+                                                                         enumSymbol.EnumUnderlyingType.Name,
+                                                                         nameof (EnumExtensionMethodsIncrementalGenerator),
+                                                                         nameof (GenerateEnumExtensionMethodsAttribute)
+                                                                        )
+                                                     );
+                }
+
+                // Check enum namespace (only non-global supported, currently)
+                // Report TG0001 if in the global namespace.
+                if (enumSymbol.ContainingSymbol is not INamespaceSymbol { IsGlobalNamespace: false })
+                {
+                    analysisContext.ReportDiagnostic (
+                                                      Diagnostic.Create (
+                                                                         TG0001_GlobalNamespaceNotSupported,
+                                                                         enumSymbol.Locations.FirstOrDefault (),
+                                                                         enumSymbol.Name,
+                                                                         nameof (EnumExtensionMethodsIncrementalGenerator),
+                                                                         nameof (GenerateEnumExtensionMethodsAttribute)
+                                                                        )
+                                                     );
+                }
+            }
+        }
+    }
+}

+ 3 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/ApiCompatExcludedAttributes.txt

@@ -0,0 +1,3 @@
+N:System.Runtime.CompilerServices
+N:System.Diagnostics.CodeAnalysis
+N:System.Numerics

+ 26 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs

@@ -0,0 +1,26 @@
+// ReSharper disable ClassNeverInstantiated.Global
+#nullable enable
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>Assembly attribute declaring a known pairing of an <see langword="enum" /> type to an extension class.</summary>
+/// <remarks>This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported.</remarks>
+[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)]
+internal sealed class AssemblyExtendedEnumTypeAttribute : System.Attribute
+{
+    /// <summary>Creates a new instance of <see cref="AssemblyExtendedEnumTypeAttribute" /> from the provided parameters.</summary>
+    /// <param name="enumType">The <see cref="System.Type" /> of an <see langword="enum" /> decorated with a <see cref="GenerateEnumExtensionMethodsAttribute" />.</param>
+    /// <param name="extensionClass">The <see cref="System.Type" /> of the <see langword="class" /> decorated with an <see cref="ExtensionsForEnumTypeAttribute{TEnum}" /> referring to the same type as <paramref name="enumType" />.</param>
+    public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass)
+    {
+        EnumType = enumType;
+        ExtensionClass = extensionClass;
+    }
+    ///<summary>An <see langword="enum" /> type that has been extended by Terminal.Gui source generators.</summary>
+    public System.Type EnumType { get; init; }
+    ///<summary>A class containing extension methods for <see cref="EnumType"/>.</summary>
+    public System.Type ExtensionClass { get; init; }
+
+    /// <inheritdoc />
+    public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}";
+}

+ 22 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/CombinationGroupingAttribute.cs

@@ -0,0 +1,22 @@
+using System;
+using JetBrains.Annotations;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
+///     this attribute which have the same <see cref="GroupTag"/> value.<br/>
+/// </summary>
+/// <remarks>
+///     This attribute is only considered for members of enum types which have the
+///     <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+/// </remarks>
+[AttributeUsage (AttributeTargets.Field)]
+[UsedImplicitly]
+internal sealed class CombinationGroupingAttribute : Attribute
+{
+    /// <summary>
+    ///     Name of a group this member participates in, for FastHasFlags.
+    /// </summary>
+    public string GroupTag { get; set; }
+}

+ 37 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs

@@ -0,0 +1,37 @@
+// ReSharper disable RedundantNameQualifier
+// ReSharper disable RedundantNullableDirective
+// ReSharper disable UnusedType.Global
+#pragma warning disable IDE0001, IDE0240
+#nullable enable
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Attribute written by the source generator for <see langword="enum" /> extension classes, for easier analysis and reflection.
+/// </summary>
+/// <remarks>
+///     Properties are just convenient shortcuts to properties of <typeparamref name="TEnum"/>.
+/// </remarks>
+[System.AttributeUsage (System.AttributeTargets.Class | System.AttributeTargets.Interface)]
+internal sealed class ExtensionsForEnumTypeAttribute<TEnum>: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, System.Enum
+{
+    /// <summary>
+    ///     The namespace-qualified name of <typeparamref name="TEnum"/>.
+    /// </summary>
+    public string EnumFullName => EnumType.FullName!;
+
+    /// <summary>
+    ///     The unqualified name of <typeparamref name="TEnum"/>.
+    /// </summary>
+    public string EnumName => EnumType.Name;
+
+    /// <summary>
+    ///     The namespace containing <typeparamref name="TEnum"/>.
+    /// </summary>
+    public string EnumNamespace => EnumType.Namespace!;
+
+    /// <summary>
+    ///     The <see cref="System.Type"/> given by <see langword="typeof"/>(<typeparamref name="TEnum"/>).
+    /// </summary>
+    public System.Type EnumType => typeof (TEnum);
+}

+ 110 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs

@@ -0,0 +1,110 @@
+// ReSharper disable RedundantNullableDirective
+// ReSharper disable RedundantUsingDirective
+// ReSharper disable ClassNeverInstantiated.Global
+
+#nullable enable
+using System;
+using Attribute = System.Attribute;
+using AttributeUsageAttribute = System.AttributeUsageAttribute;
+using AttributeTargets = System.AttributeTargets;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Used to enable source generation of a common set of extension methods for enum types.
+/// </summary>
+[AttributeUsage (AttributeTargets.Enum)]
+internal sealed class GenerateEnumExtensionMethodsAttribute : Attribute
+{
+    /// <summary>
+    ///     The name of the generated static class.
+    /// </summary>
+    /// <remarks>
+    ///     If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".<br/>
+    ///     No other validation is performed, so illegal values will simply result in compiler errors.
+    ///     <para>
+    ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+    ///     </para>
+    /// </remarks>
+    public string? ClassName { get; set; }
+
+    /// <summary>
+    ///     The namespace in which to place the generated static class containing the extension methods.
+    /// </summary>
+    /// <remarks>
+    ///     If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.<br/>
+    ///     No other validation is performed, so illegal values will simply result in compiler errors.
+    ///     <para>
+    ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+    ///     </para>
+    /// </remarks>
+    public string? ClassNamespace { get; set; }
+
+    /// <summary>
+    ///     Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in
+    ///     <see cref="Enum.HasFlag"/> method.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Default: false
+    ///     </para>
+    ///     <para>
+    ///         If the enum is not decorated with <see cref="FlagsAttribute"/>, this option has no effect.
+    ///     </para>
+    ///     <para>
+    ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+    ///         with the same value will be skipped.
+    ///     </para>
+    ///     <para>
+    ///         Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling
+    ///         avoidance of implicit or explicit cast overhead.
+    ///     </para>
+    ///     <para>
+    ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+    ///     </para>
+    /// </remarks>
+    public bool FastHasFlags { get; set; }
+
+    /// <summary>
+    ///     Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in
+    ///     <see cref="Enum.IsDefined"/> method,
+    ///     using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Default: true
+    ///     </para>
+    ///     <para>
+    ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+    ///         with the same value will be skipped.
+    ///     </para>
+    ///     <para>
+    ///         As with <see cref="Enum.IsDefined"/> the source generator only considers explicitly-named members.<br/>
+    ///         Generation of values which represent valid bitwise combinations of members of enums decorated with
+    ///         <see cref="FlagsAttribute"/> is not affected by this property.
+    ///     </para>
+    /// </remarks>
+    public bool FastIsDefined { get; init; } = true;
+
+    /// <summary>
+    ///     Gets a <see langword="bool"/> value indicating if this <see cref="GenerateEnumExtensionMethodsAttribute"/> instance
+    ///     contains default values only. See <see href="#remarks">remarks</see> of this method or documentation on properties of this type for details.
+    /// </summary>
+    /// <returns>
+    ///     A <see langword="bool"/> value indicating if all property values are default for this
+    ///     <see cref="GenerateEnumExtensionMethodsAttribute"/> instance.
+    /// </returns>
+    /// <remarks>
+    ///     Default values that will result in a <see langword="true"/> return value are:<br/>
+    ///     <see cref="FastIsDefined"/> &amp;&amp; !<see cref="FastHasFlags"/> &amp;&amp; <see cref="ClassName"/>
+    ///     <see langword="is"/> <see langword="null"/> &amp;&amp; <see cref="ClassNamespace"/> <see langword="is"/>
+    ///     <see langword="null"/>
+    /// </remarks>
+    public override bool IsDefaultAttribute ()
+    {
+        return FastIsDefined
+               && !FastHasFlags
+               && ClassName is null
+               && ClassNamespace is null;
+    }
+}

+ 110 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumMemberCombinationsAttribute.cs

@@ -0,0 +1,110 @@
+// ReSharper disable RedundantUsingDirective
+
+using System;
+using JetBrains.Annotations;
+using Terminal.Gui.Analyzers.Internal.Compatibility;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
+///     this attribute which have the same <see cref="GroupTag"/> value.<br/>
+/// </summary>
+/// <remarks>
+///     <para>
+///         This attribute is only considered for enum types with the <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+///     </para>
+/// </remarks>
+[AttributeUsage (AttributeTargets.Enum)]
+[UsedImplicitly]
+public sealed class GenerateEnumMemberCombinationsAttribute : System.Attribute
+{
+    private const byte MaximumPopCountLimit = 14;
+    private uint _mask;
+    private uint _maskPopCount;
+    private byte _popCountLimit = 8;
+    /// <inheritdoc cref="CombinationGroupingAttribute.GroupTag" />
+    public string GroupTag { get; set; }
+
+    /// <summary>
+    /// The mask for the group defined in <see cref="GroupTag"/>
+    /// </summary>
+    public uint Mask
+    {
+        get => _mask;
+        set
+        {
+#if NET8_0_OR_GREATER
+            _maskPopCount = uint.PopCount (value);
+#else
+            _maskPopCount = value.GetPopCount ();
+#endif
+            PopCountLimitExceeded = _maskPopCount > PopCountLimit;
+            MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+
+            if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+            {
+                return;
+            }
+
+            _mask = value;
+        }
+    }
+
+    /// <summary>
+    ///     The maximum number of bits allowed to be set to 1 in <see cref="Mask"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Default: 8 (256 possible combinations)
+    ///     </para>
+    ///     <para>
+    ///         Increasing this value is not recommended!<br/>
+    ///         Decreasing this value is pointless unless you want to limit maximum possible generated combinations even
+    ///         further.
+    ///     </para>
+    ///     <para>
+    ///         If the result of <see cref="NumericExtensions.GetPopCount(uint)"/>(<see cref="Mask"/>) exceeds 2 ^
+    ///         <see cref="PopCountLimit"/>, no
+    ///         combinations will be generated for the members which otherwise would have been included by <see cref="Mask"/>.
+    ///         Values exceeding the actual population count of <see cref="Mask"/> have no effect.
+    ///     </para>
+    ///     <para>
+    ///         This option is set to a sane default of 8, but also has a hard-coded limit of 14 (16384 combinations), as a
+    ///         protection against generation of extremely large files.
+    ///     </para>
+    ///     <para>
+    ///         CAUTION: The maximum number of possible combinations possible is equal to 1 &lt;&lt;
+    ///         <see cref="NumericExtensions.GetPopCount(uint)"/>(<see cref="Mask"/>).
+    ///         See <see cref="MaximumPopCountLimit"/> for hard-coded limit,
+    ///     </para>
+    /// </remarks>
+    public byte PopCountLimit
+    {
+        get => _popCountLimit;
+        set
+        {
+#if NET8_0_OR_GREATER
+            _maskPopCount = uint.PopCount (_mask);
+#else
+            _maskPopCount = _mask.GetPopCount ();
+#endif
+
+            PopCountLimitExceeded = _maskPopCount > value;
+            MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+
+            if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+            {
+                return;
+            }
+
+            _mask = value;
+            _popCountLimit = value;
+        }
+    }
+
+    [UsedImplicitly]
+    internal bool MaximumPopCountLimitExceeded { get; private set; }
+    [UsedImplicitly]
+    internal bool PopCountLimitExceeded { get; private set; }
+}

+ 14 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs

@@ -0,0 +1,14 @@
+// ReSharper disable All
+
+using System;
+
+namespace Terminal.Gui.Analyzers.Internal.Attributes;
+
+/// <summary>
+///     Interface to simplify general enumeration of constructed generic types for
+///     <see cref="ExtensionsForEnumTypeAttribute{TEnum}"/>
+/// </summary>
+internal interface IExtensionsForEnumTypeAttributes
+{
+    Type EnumType { get; }
+}

+ 33 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/CompilerFeatureRequiredAttribute.cs

@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+namespace System.Runtime.CompilerServices;
+
+/// <summary>
+///     Indicates that compiler support for a particular feature is required for the location where this attribute is
+///     applied.
+/// </summary>
+[AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+internal sealed class CompilerFeatureRequiredAttribute(string featureName) : Attribute
+{
+    /// <summary>
+    ///     The <see cref="FeatureName"/> used for the ref structs C# feature.
+    /// </summary>
+    public const string RefStructs = nameof (RefStructs);
+
+    /// <summary>
+    ///     The <see cref="FeatureName"/> used for the required members C# feature.
+    /// </summary>
+    public const string RequiredMembers = nameof (RequiredMembers);
+
+    /// <summary>
+    ///     The name of the compiler feature.
+    /// </summary>
+    public string FeatureName { get; } = featureName;
+    /// <summary>
+    ///     If true, the compiler can choose to allow access to the location where this attribute is applied if it does not
+    ///     understand <see cref="FeatureName"/>.
+    /// </summary>
+    public bool IsOptional { get; init; }
+}

+ 11 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs

@@ -0,0 +1,11 @@
+// ReSharper disable once CheckNamespace
+namespace System.Numerics;
+/// <summary>
+/// Included for compatibility with .net7+, but has no members.
+/// Thus it cannot be explicitly used in generator code.
+/// Use it for static analysis only.
+/// </summary>
+/// <typeparam name="T">The left operand type.</typeparam>
+/// <typeparam name="T1">The right operand type.</typeparam>
+/// <typeparam name="T2">The return type.</typeparam>
+internal interface IEqualityOperators<T, T1, T2>;

+ 6 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs

@@ -0,0 +1,6 @@
+namespace System.Runtime.CompilerServices;
+
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)]
+public sealed class IntrinsicAttribute : Attribute
+{
+}

+ 18 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IsExternalInit.cs

@@ -0,0 +1,18 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+// ReSharper disable CheckNamespace
+namespace System.Runtime.CompilerServices;
+
+/// <summary>
+///     Reserved to be used by the compiler for tracking metadata.
+///     This class should not be used by developers in source code.
+/// </summary>
+/// <remarks>
+///     Copied from .net source code, for support of init property accessors in netstandard2.0.
+/// </remarks>
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[EditorBrowsable (EditorBrowsableState.Never)]
+public static class IsExternalInit;

+ 208 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NullableAttributes.cs

@@ -0,0 +1,208 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// This file is further modified from the original, for this project,
+// to comply with project style.
+// No changes are made which affect compatibility with the same types from
+// APIs later than netstandard2.0, nor will this file be included in compilations
+// targeted at later APIs.
+//
+// Originally rom https://github.com/dotnet/runtime/blob/ef72b95937703e485fdbbb75f3251fedfd1a0ef9/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
+
+// ReSharper disable CheckNamespace
+
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+// ReSharper disable UnusedType.Global
+
+namespace System.Diagnostics.CodeAnalysis;
+
+/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class AllowNullAttribute : Attribute;
+
+/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class DisallowNullAttribute : Attribute;
+
+/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MaybeNullAttribute : Attribute;
+
+/// <summary>
+///     Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input
+///     argument was not null when the call returns.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class NotNullAttribute : Attribute;
+
+/// <summary>
+///     Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding
+///     type disallows it.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MaybeNullWhenAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified return value condition.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter may be null.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public MaybeNullWhenAttribute (bool returnValue) { ReturnValue = returnValue; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the return value condition.</summary>
+    public bool ReturnValue { get; }
+}
+
+/// <summary>
+///     Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the
+///     corresponding type allows it.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class NotNullWhenAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified return value condition.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter will not be null.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public NotNullWhenAttribute (bool returnValue) { ReturnValue = returnValue; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the return value condition.</summary>
+    public bool ReturnValue { get; }
+}
+
+/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class NotNullIfNotNullAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the associated parameter name.</summary>
+    /// <param name="parameterName">
+    ///     The associated parameter name.  The output will be non-null if the argument to the parameter specified is non-null.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public NotNullIfNotNullAttribute (string parameterName) { ParameterName = parameterName; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the associated parameter name.</summary>
+    public string ParameterName { get; }
+}
+
+/// <summary>Applied to a method that will never return under any circumstance.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Method, Inherited = false)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class DoesNotReturnAttribute : Attribute;
+
+/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Parameter)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class DoesNotReturnIfAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified parameter value.</summary>
+    /// <param name="parameterValue">
+    ///     The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument
+    ///     to
+    ///     the associated parameter matches this value.
+    /// </param>
+#pragma warning disable IDE0290 // Use primary constructor
+    public DoesNotReturnIfAttribute (bool parameterValue) { ParameterValue = parameterValue; }
+#pragma warning restore IDE0290 // Use primary constructor
+
+    /// <summary>Gets the condition parameter value.</summary>
+    public bool ParameterValue { get; }
+}
+
+/// <summary>
+///     Specifies that the method or property will ensure that the listed field and property members have not-null
+///     values.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MemberNotNullAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with a field or property member.</summary>
+    /// <param name="member">
+    ///     The field or property member that is promised to be not-null.
+    /// </param>
+    public MemberNotNullAttribute (string member) { Members = [member]; }
+
+    /// <summary>Initializes the attribute with the list of field and property members.</summary>
+    /// <param name="members">
+    ///     The list of field and property members that are promised to be not-null.
+    /// </param>
+    public MemberNotNullAttribute (params string [] members) { Members = members; }
+
+    /// <summary>Gets field or property member names.</summary>
+    public string [] Members { get; }
+}
+
+/// <summary>
+///     Specifies that the method or property will ensure that the listed field and property members have not-null values
+///     when returning with the specified return value condition.
+/// </summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal sealed class MemberNotNullWhenAttribute : Attribute
+{
+    /// <summary>Initializes the attribute with the specified return value condition and a field or property member.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter will not be null.
+    /// </param>
+    /// <param name="member">
+    ///     The field or property member that is promised to be not-null.
+    /// </param>
+    public MemberNotNullWhenAttribute (bool returnValue, string member)
+    {
+        ReturnValue = returnValue;
+        Members = [member];
+    }
+
+    /// <summary>Initializes the attribute with the specified return value condition and list of field and property members.</summary>
+    /// <param name="returnValue">
+    ///     The return value condition. If the method returns this value, the associated parameter will not be null.
+    /// </param>
+    /// <param name="members">
+    ///     The list of field and property members that are promised to be not-null.
+    /// </param>
+    public MemberNotNullWhenAttribute (bool returnValue, params string [] members)
+    {
+        ReturnValue = returnValue;
+        Members = members;
+    }
+
+    /// <summary>Gets field or property member names.</summary>
+    public string [] Members { get; }
+
+    /// <summary>Gets the return value condition.</summary>
+    public bool ReturnValue { get; }
+}

+ 43 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs

@@ -0,0 +1,43 @@
+// ReSharper disable once CheckNamespace
+namespace Terminal.Gui.Analyzers.Internal.Compatibility;
+
+/// <summary>
+///     Extension methods for <see langword="int"/> and <see langword="uint"/> types.
+/// </summary>
+/// <remarks>
+///     This is mostly just for backward compatibility with netstandard2.0.
+/// </remarks>
+public static class NumericExtensions
+{
+    /// <summary>
+    ///     Gets the population count (number of bits set to 1) of this 32-bit value.
+    /// </summary>
+    /// <param name="value">The value to get the population count of.</param>
+    /// <remarks>
+    ///     The algorithm is the well-known SWAR (SIMD Within A Register) method for population count.<br/>
+    ///     Included for hardware- and runtime- agnostic support for the equivalent of the x86 popcnt instruction, since
+    ///     System.Numerics.Intrinsics isn't available in netstandard2.0.<br/>
+    ///     It performs the operation simultaneously on 4 bytes at a time, rather than the naive method of testing all 32 bits
+    ///     individually.<br/>
+    ///     Most compilers can recognize this and turn it into a single platform-specific instruction, when available.
+    /// </remarks>
+    /// <returns>
+    ///     An unsigned 32-bit integer value containing the population count of <paramref name="value"/>.
+    /// </returns>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static uint GetPopCount (this uint value)
+    {
+        unchecked
+        {
+            value -= (value >> 1) & 0x55555555;
+            value = (value & 0x33333333) + ((value >> 2) & 0x33333333);
+            value = (value + (value >> 4)) & 0x0F0F0F0F;
+
+            return (value * 0x01010101) >> 24;
+        }
+    }
+
+    /// <inheritdoc cref="GetPopCount(uint)"/>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static uint GetPopCount (this int value) { return GetPopCount (Unsafe.As<int, uint> (ref value)); }
+}

+ 12 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/RequiredMemberAttribute.cs

@@ -0,0 +1,12 @@
+// ReSharper disable CheckNamespace
+// ReSharper disable ConditionalAnnotation
+
+using JetBrains.Annotations;
+
+namespace System.Runtime.CompilerServices;
+
+/// <summary>Polyfill to enable netstandard2.0 assembly to use the required keyword.</summary>
+/// <remarks>Excluded from output assembly via file specified in ApiCompatExcludeAttributesFile element in the project file.</remarks>
+[AttributeUsage (AttributeTargets.Property)]
+[UsedImplicitly]
+public sealed class RequiredMemberAttribute : Attribute;

+ 12 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/SetsRequiredMembersAttribute.cs

@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+namespace System.Diagnostics.CodeAnalysis;
+
+/// <summary>
+///     Specifies that this constructor sets all required members for the current type, and callers
+///     do not need to set any required members themselves.
+/// </summary>
+[AttributeUsage (AttributeTargets.Constructor)]
+public sealed class SetsRequiredMembersAttribute : Attribute;

+ 14 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/SkipLocalsInitAttribute.cs

@@ -0,0 +1,14 @@
+namespace System.Runtime.CompilerServices;
+
+[AttributeUsage (
+                    AttributeTargets.Class
+                    | AttributeTargets.Constructor
+                    | AttributeTargets.Event
+                    | AttributeTargets.Interface
+                    | AttributeTargets.Method
+                    | AttributeTargets.Module
+                    | AttributeTargets.Property
+                    | AttributeTargets.Struct,
+                    Inherited = false)]
+
+internal sealed class SkipLocalsInitAttribute : Attribute;

+ 202 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs

@@ -0,0 +1,202 @@
+// ReSharper disable MemberCanBePrivate.Global
+
+using System;
+using System.CodeDom.Compiler;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui.Analyzers.Internal.Constants;
+
+/// <summary>String constants for frequently-used boilerplate.</summary>
+/// <remarks>These are for performance, instead of using Roslyn to build it all during execution of analyzers.</remarks>
+internal static class Strings
+{
+    internal const string AnalyzersAttributesNamespace = $"{InternalAnalyzersNamespace}.Attributes";
+
+    internal const string AssemblyExtendedEnumTypeAttributeFullName = $"{AnalyzersAttributesNamespace}.{nameof (AssemblyExtendedEnumTypeAttribute)}";
+
+    internal const string DefaultTypeNameSuffix = "Extensions";
+
+    internal const string FallbackClassNamespace = $"{TerminalGuiRootNamespace}";
+
+    internal const string InternalAnalyzersNamespace = $"{AnalyzersRootNamespace}.Internal";
+
+    internal const string TerminalGuiRootNamespace = "Terminal.Gui";
+
+    private const string AnalyzersRootNamespace = $"{TerminalGuiRootNamespace}.Analyzers";
+    private const string NetStandard20CompatibilityNamespace = $"{InternalAnalyzersNamespace}.Compatibility";
+
+    /// <summary>
+    ///     Names of dotnet namespaces and types. Included as compile-time constants to avoid unnecessary work for the Roslyn
+    ///     source generators.
+    /// </summary>
+    /// <remarks>Implemented as nested static types because XmlDoc doesn't work on namespaces.</remarks>
+    internal static class DotnetNames
+    {
+        /// <summary>Fully-qualified attribute type names. Specific applications (uses) are in <see cref="Applications"/>.</summary>
+        internal static class Attributes
+        {
+            /// <inheritdoc cref="CompilerGeneratedAttribute"/>
+            internal const string CompilerGenerated = $"{Namespaces.System_Runtime_CompilerServices}.{nameof (CompilerGeneratedAttribute)}";
+
+            /// <inheritdoc cref="DebuggerNonUserCodeAttribute"/>
+            internal const string DebuggerNonUserCode = $"{Namespaces.System_Diagnostics}.{nameof (DebuggerNonUserCodeAttribute)}";
+
+            /// <inheritdoc cref="ExcludeFromCodeCoverageAttribute"/>
+            internal const string ExcludeFromCodeCoverage = $"{Namespaces.System_Diagnostics_CodeAnalysis}.{nameof (ExcludeFromCodeCoverageAttribute)}";
+
+            internal const string Flags = $"{Namespaces.SystemNS}.{nameof (FlagsAttribute)}";
+
+            internal const string GeneratedCode = $"{Namespaces.System_CodeDom_Compiler}.{nameof (GeneratedCodeAttribute)}";
+
+            /// <inheritdoc cref="MethodImplOptions.AggressiveInlining"/>
+            /// <remarks>Use of this attribute should be carefully evaluated.</remarks>
+            internal const string MethodImpl = $"{Namespaces.System_Runtime_CompilerServices}.{nameof (MethodImplAttribute)}";
+
+            /// <summary>Attributes formatted for use in code, including square brackets.</summary>
+            internal static class Applications
+            {
+                // ReSharper disable MemberHidesStaticFromOuterClass
+                internal const string Flags = $"[{Attributes.Flags}]";
+
+                /// <inheritdoc cref="System.CodeDom.Compiler.GeneratedCodeAttribute"/>
+                internal const string GeneratedCode = $"""[{Attributes.GeneratedCode}("{InternalAnalyzersNamespace}","1.0")]""";
+
+                /// <inheritdoc cref="MethodImplOptions.AggressiveInlining"/>
+                /// <remarks>Use of this attribute should be carefully evaluated.</remarks>
+                internal const string AggressiveInlining = $"[{MethodImpl}({Types.MethodImplOptions}.{nameof (MethodImplOptions.AggressiveInlining)})]";
+
+                /// <inheritdoc cref="DebuggerNonUserCodeAttribute"/>
+                internal const string DebuggerNonUserCode = $"[{Attributes.DebuggerNonUserCode}]";
+
+                /// <inheritdoc cref="CompilerGeneratedAttribute"/>
+                internal const string CompilerGenerated = $"[{Attributes.CompilerGenerated}]";
+
+                /// <inheritdoc cref="ExcludeFromCodeCoverageAttribute"/>
+                internal const string ExcludeFromCodeCoverage = $"[{Attributes.ExcludeFromCodeCoverage}]";
+
+                // ReSharper restore MemberHidesStaticFromOuterClass
+            }
+        }
+
+        /// <summary>Names of dotnet namespaces.</summary>
+        internal static class Namespaces
+        {
+            internal const string SystemNS = nameof (System);
+            internal const string System_CodeDom = $"{SystemNS}.{nameof (System.CodeDom)}";
+            internal const string System_CodeDom_Compiler = $"{System_CodeDom}.{nameof (System.CodeDom.Compiler)}";
+            internal const string System_ComponentModel = $"{SystemNS}.{nameof (System.ComponentModel)}";
+            internal const string System_Diagnostics = $"{SystemNS}.{nameof (System.Diagnostics)}";
+            internal const string System_Diagnostics_CodeAnalysis = $"{System_Diagnostics}.{nameof (System.Diagnostics.CodeAnalysis)}";
+            internal const string System_Numerics = $"{SystemNS}.{nameof (System.Numerics)}";
+            internal const string System_Runtime = $"{SystemNS}.{nameof (System.Runtime)}";
+            internal const string System_Runtime_CompilerServices = $"{System_Runtime}.{nameof (System.Runtime.CompilerServices)}";
+        }
+
+        internal static class Types
+        {
+            internal const string Attribute = $"{Namespaces.SystemNS}.{nameof (System.Attribute)}";
+            internal const string AttributeTargets = $"{Namespaces.SystemNS}.{nameof (System.AttributeTargets)}";
+            internal const string AttributeUsageAttribute = $"{Namespaces.SystemNS}.{nameof (System.AttributeUsageAttribute)}";
+
+            internal const string MethodImplOptions =
+                $"{Namespaces.System_Runtime_CompilerServices}.{nameof (System.Runtime.CompilerServices.MethodImplOptions)}";
+        }
+    }
+
+    internal static class Templates
+    {
+        internal const string AutoGeneratedCommentBlock = $"""
+                                                           //------------------------------------------------------------------------------
+                                                           // <auto-generated>
+                                                           //   This file and the code it contains was generated by a source generator in
+                                                           //   the {InternalAnalyzersNamespace} library.
+                                                           //
+                                                           //   Modifications to this file are not supported and will be lost when
+                                                           //   source generation is triggered, either implicitly or explicitly.
+                                                           // </auto-generated>
+                                                           //------------------------------------------------------------------------------
+                                                           """;
+
+        /// <summary>
+        ///     A set of explicit type aliases to work around Terminal.Gui having name collisions with types like
+        ///     <see cref="System.Attribute"/>.
+        /// </summary>
+        internal const string DotnetExplicitTypeAliasUsingDirectives = $"""
+                                                                        using Attribute = {DotnetNames.Types.Attribute};
+                                                                        using AttributeUsageAttribute = {DotnetNames.Types.AttributeUsageAttribute};
+                                                                        using GeneratedCode = {DotnetNames.Attributes.GeneratedCode};
+                                                                        """;
+
+        /// <summary>Using directives for common namespaces in generated code.</summary>
+        internal const string DotnetNamespaceUsingDirectives = $"""
+                                                                using {DotnetNames.Namespaces.SystemNS};
+                                                                using {DotnetNames.Namespaces.System_CodeDom};
+                                                                using {DotnetNames.Namespaces.System_CodeDom_Compiler};
+                                                                using {DotnetNames.Namespaces.System_ComponentModel};
+                                                                using {DotnetNames.Namespaces.System_Numerics};
+                                                                using {DotnetNames.Namespaces.System_Runtime};
+                                                                using {DotnetNames.Namespaces.System_Runtime_CompilerServices};
+                                                                """;
+
+        /// <summary>
+        ///     A set of empty namespaces that MAY be referenced in generated code, especially in using statements,
+        ///     which are always included to avoid additional complexity due to conditional compilation.
+        /// </summary>
+        internal const string DummyNamespaceDeclarations = $$"""
+                                                             // These are dummy declarations to avoid complexity with conditional compilation.
+                                                             #pragma warning disable IDE0079 // Remove unnecessary suppression
+                                                             #pragma warning disable RCS1259 // Remove empty syntax
+                                                             namespace {{TerminalGuiRootNamespace}} { }
+                                                             namespace {{AnalyzersRootNamespace}} { }
+                                                             namespace {{InternalAnalyzersNamespace}} { }
+                                                             namespace {{NetStandard20CompatibilityNamespace}} { }
+                                                             namespace {{AnalyzersAttributesNamespace}} { }
+                                                             #pragma warning restore RCS1259 // Remove empty syntax
+                                                             #pragma warning restore IDE0079 // Remove unnecessary suppression
+                                                             """;
+
+        internal const string StandardHeader = $"""
+                                                {AutoGeneratedCommentBlock}
+                                                // ReSharper disable RedundantUsingDirective
+                                                // ReSharper disable once RedundantNullableDirective
+                                                {NullableContextDirective}
+
+                                                {StandardUsingDirectivesText}
+                                                """;
+
+        /// <summary>
+        ///     Standard set of using directives for generated extension method class files.
+        ///     Not all are always needed, but all are included so we don't have to worry about it.
+        /// </summary>
+        internal const string StandardUsingDirectivesText = $"""
+                                                             {DotnetNamespaceUsingDirectives}
+                                                             {DotnetExplicitTypeAliasUsingDirectives}
+                                                             using {TerminalGuiRootNamespace};
+                                                             using {AnalyzersRootNamespace};
+                                                             using {InternalAnalyzersNamespace};
+                                                             using {AnalyzersAttributesNamespace};
+                                                             using {NetStandard20CompatibilityNamespace};
+                                                             """;
+
+        internal const string AttributesForGeneratedInterfaces = $"""
+                                                                  {DotnetNames.Attributes.Applications.GeneratedCode}
+                                                                  {DotnetNames.Attributes.Applications.CompilerGenerated}
+                                                                  """;
+
+        internal const string AttributesForGeneratedTypes = $"""
+                                                             {DotnetNames.Attributes.Applications.GeneratedCode}
+                                                             {DotnetNames.Attributes.Applications.CompilerGenerated}
+                                                             {DotnetNames.Attributes.Applications.DebuggerNonUserCode}
+                                                             {DotnetNames.Attributes.Applications.ExcludeFromCodeCoverage}
+                                                             """;
+
+        /// <summary>
+        ///     Preprocessor directive to enable nullability context for generated code.<br/>
+        ///     This should always be emitted, as it applies only to generated code.<br/>
+        ///     As such, generated code MUST be properly annotated.
+        /// </summary>
+        internal const string NullableContextDirective = "#nullable enable";
+    }
+}

+ 235 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs

@@ -0,0 +1,235 @@
+using System;
+using System.CodeDom.Compiler;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+///     The class responsible for turning an <see cref="EnumExtensionMethodsGenerationInfo"/>
+///     into actual C# code.
+/// </summary>
+/// <remarks>Try to use this type as infrequently as possible.</remarks>
+/// <param name="metadata">
+///     A reference to an <see cref="IGeneratedTypeMetadata{TSelf}"/> which will be used
+///     to generate the extension class code. The object will not be validated,
+///     so it is critical that it be correct and remain unchanged while in use
+///     by an instance of this class. Behavior if those rules are not followed
+///     is undefined.
+/// </param>
+[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")]
+internal sealed class CodeWriter (in EnumExtensionMethodsGenerationInfo metadata) : IStandardCSharpCodeGenerator<EnumExtensionMethodsGenerationInfo>
+{
+    // Using the null suppression operator here because this will always be
+    // initialized to non-null before a reference to it is returned.
+    private SourceText _sourceText = null!;
+
+    /// <inheritdoc/>
+    public EnumExtensionMethodsGenerationInfo Metadata
+    {
+        [MethodImpl (MethodImplOptions.AggressiveInlining)]
+        [return: NotNull]
+        get;
+        [param: DisallowNull]
+        set;
+    } = metadata;
+
+    /// <inheritdoc/>
+    public ref readonly SourceText GenerateSourceText (Encoding? encoding = null)
+    {
+        encoding ??= Encoding.UTF8;
+        _sourceText = SourceText.From (GetFullSourceText (), encoding);
+
+        return ref _sourceText;
+    }
+
+    /// <summary>
+    ///     Gets the using directive for the namespace containing the enum,
+    ///     if different from the extension class namespace, or an empty string, if they are the same.
+    /// </summary>
+    private string EnumNamespaceUsingDirective => Metadata.TargetTypeNamespace != Metadata.GeneratedTypeNamespace
+
+                                                      // ReSharper disable once HeapView.ObjectAllocation
+                                                      ? $"using {Metadata.TargetTypeNamespace};"
+                                                      : string.Empty;
+
+    private string EnumTypeKeyword => Metadata.EnumBackingTypeCode switch
+                                      {
+                                          TypeCode.Int32 => "int",
+                                          TypeCode.UInt32 => "uint",
+                                          _ => string.Empty
+                                      };
+
+    /// <summary>Gets the class declaration line.</summary>
+    private string ExtensionClassDeclarationLine => $"public static partial class {Metadata.GeneratedTypeName}";
+
+    // ReSharper disable once HeapView.ObjectAllocation
+    /// <summary>Gets the XmlDoc for the extension class declaration.</summary>
+    private string ExtensionClassDeclarationXmlDoc =>
+        $"/// <summary>Extension methods for the <see cref=\"{Metadata.TargetTypeFullName}\"/> <see langword=\"enum\" /> type.</summary>";
+
+    // ReSharper disable once HeapView.ObjectAllocation
+    /// <summary>Gets the extension class file-scoped namespace directive.</summary>
+    private string ExtensionClassNamespaceDirective => $"namespace {Metadata.GeneratedTypeNamespace};";
+
+    /// <summary>
+    ///     An attribute to decorate the extension class with for easy mapping back to the target enum type, for reflection and
+    ///     analysis.
+    /// </summary>
+    private string ExtensionsForTypeAttributeLine => $"[ExtensionsForEnumType<{Metadata.TargetTypeFullName}>]";
+
+    /// <summary>
+    ///     Creates the code for the FastHasFlags method.
+    /// </summary>
+    /// <remarks>
+    ///     Since the generator already only writes code for enums backed by <see langword="int"/> and <see langword="uint"/>,
+    ///     this method is safe, as we'll always be using a DWORD.
+    /// </remarks>
+    /// <param name="w">An instance of an <see cref="IndentedTextWriter"/> to write to.</param>
+    private void GetFastHasFlagsMethods (IndentedTextWriter w)
+    {
+        // The version taking the same enum type as the check value.
+        w.WriteLine (
+                     $"/// <summary>Determines if the specified flags are set in the current value of this <see cref=\"{Metadata.TargetTypeFullName}\" />.</summary>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+
+        w.WriteLine (
+                     $"/// <returns>True, if all flags present in <paramref name=\"checkFlags\" /> are also present in the current value of the <see cref=\"{Metadata.TargetTypeFullName}\" />.<br />Otherwise false.</returns>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+
+        w.Push (
+                $"{Metadata.Accessibility.ToCSharpString ()} static bool FastHasFlags (this {Metadata.TargetTypeFullName} e, {Metadata.TargetTypeFullName} checkFlags)");
+        w.WriteLine ($"ref uint enumCurrentValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref e);");
+        w.WriteLine ($"ref uint checkFlagsValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref checkFlags);");
+        w.WriteLine ("return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef;");
+        w.Pop ();
+
+        // The version taking the underlying type of the enum as the check value.
+        w.WriteLine (
+                     $"/// <summary>Determines if the specified mask bits are set in the current value of this <see cref=\"{Metadata.TargetTypeFullName}\" />.</summary>");
+
+        w.WriteLine (
+                     $"/// <param name=\"e\">The <see cref=\"{Metadata.TargetTypeFullName}\" /> value to check against the <paramref name=\"mask\" /> value.</param>");
+        w.WriteLine ("/// <param name=\"mask\">A mask to apply to the current value.</param>");
+
+        w.WriteLine (
+                     $"/// <returns>True, if all bits set to 1 in the mask are also set to 1 in the current value of the <see cref=\"{Metadata.TargetTypeFullName}\" />.<br />Otherwise false.</returns>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+
+        w.Push (
+                $"{Metadata.Accessibility.ToCSharpString ()} static bool FastHasFlags (this {Metadata.TargetTypeFullName} e, {EnumTypeKeyword} mask)");
+        w.WriteLine ($"ref {EnumTypeKeyword} enumCurrentValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},{EnumTypeKeyword}> (ref e);");
+        w.WriteLine ("return (enumCurrentValueRef & mask) == mask;");
+        w.Pop ();
+    }
+
+    /// <summary>
+    ///     Creates the code for the FastIsDefined method.
+    /// </summary>
+    [SuppressMessage ("ReSharper", "SwitchStatementHandlesSomeKnownEnumValuesWithDefault", Justification = "Only need to handle int and uint.")]
+    [SuppressMessage ("ReSharper", "SwitchStatementMissingSomeEnumCasesNoDefault", Justification = "Only need to handle int and uint.")]
+    private void GetFastIsDefinedMethod (IndentedTextWriter w)
+    {
+        w.WriteLine (
+                     $"/// <summary>Determines if the specified <see langword=\"{EnumTypeKeyword}\" /> value is explicitly defined as a named value of the <see cref=\"{Metadata.TargetTypeFullName}\" /> <see langword=\"enum\" /> type.</summary>");
+
+        w.WriteLine (
+                     "/// <remarks>Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are not explicitly named will return false.</remarks>");
+
+        w.Push (
+                $"{Metadata.Accessibility.ToCSharpString ()} static bool FastIsDefined (this {Metadata.TargetTypeFullName} e, {EnumTypeKeyword} value)");
+        w.Push ("return value switch");
+
+        switch (Metadata.EnumBackingTypeCode)
+        {
+            case TypeCode.Int32:
+                foreach (int definedValue in Metadata.IntMembers)
+                {
+                    w.WriteLine ($"{definedValue:D} => true,");
+                }
+
+                break;
+            case TypeCode.UInt32:
+                foreach (uint definedValue in Metadata.UIntMembers)
+                {
+                    w.WriteLine ($"{definedValue:D} => true,");
+                }
+
+                break;
+        }
+
+        w.WriteLine ("_ => false");
+
+        w.Pop ("};");
+        w.Pop ();
+    }
+
+    private string GetFullSourceText ()
+    {
+        StringBuilder sb = new (
+                                $"""
+                                 {Strings.Templates.StandardHeader}
+
+                                 [assembly: {Strings.AssemblyExtendedEnumTypeAttributeFullName} (typeof({Metadata.TargetTypeFullName}), typeof({Metadata.GeneratedTypeFullName}))]
+
+                                 {EnumNamespaceUsingDirective}
+                                 {ExtensionClassNamespaceDirective}
+                                 {ExtensionClassDeclarationXmlDoc}
+                                 {Strings.Templates.AttributesForGeneratedTypes}
+                                 {ExtensionsForTypeAttributeLine}
+                                 {ExtensionClassDeclarationLine}
+                                 
+                                 """,
+                                4096);
+
+        using IndentedTextWriter w = new (new StringWriter (sb));
+        w.Push ();
+
+        GetNamedValuesToInt32Method (w);
+        GetNamedValuesToUInt32Method (w);
+
+        if (Metadata.GenerateFastIsDefined)
+        {
+            GetFastIsDefinedMethod (w);
+        }
+
+        if (Metadata.GenerateFastHasFlags)
+        {
+            GetFastHasFlagsMethods (w);
+        }
+
+        w.Pop ();
+
+        w.Flush ();
+
+        return sb.ToString ();
+    }
+
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    private void GetNamedValuesToInt32Method (IndentedTextWriter w)
+    {
+        w.WriteLine (
+                     $"/// <summary>Directly converts this <see cref=\"{Metadata.TargetTypeFullName}\" /> value to an <see langword=\"int\" /> value with the same binary representation.</summary>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+        w.Push ($"{Metadata.Accessibility.ToCSharpString ()} static int AsInt32 (this {Metadata.TargetTypeFullName} e)");
+        w.WriteLine ($"return Unsafe.As<{Metadata.TargetTypeFullName},int> (ref e);");
+        w.Pop ();
+    }
+
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    private void GetNamedValuesToUInt32Method (IndentedTextWriter w)
+    {
+        w.WriteLine (
+                     $"/// <summary>Directly converts this <see cref=\"{Metadata.TargetTypeFullName}\" /> value to a <see langword=\"uint\" /> value with the same binary representation.</summary>");
+        w.WriteLine ("/// <remarks>NO VALIDATION IS PERFORMED!</remarks>");
+        w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining);
+        w.Push ($"{Metadata.Accessibility.ToCSharpString ()} static uint AsUInt32 (this {Metadata.TargetTypeFullName} e)");
+        w.WriteLine ($"return Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref e);");
+        w.Pop ();
+    }
+}

+ 457 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs

@@ -0,0 +1,457 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+///     Type containing the information necessary to generate code according to the declared attribute values,
+///     as well as the actual code to create the corresponding source code text, to be used in the
+///     source generator pipeline.
+/// </summary>
+/// <remarks>
+///     Minimal validation is performed by this type.<br/>
+///     Errors in analyzed source code will result in generation failure or broken output.<br/>
+///     This type is not intended for use outside of Terminal.Gui library development.
+/// </remarks>
+internal sealed record EnumExtensionMethodsGenerationInfo : IGeneratedTypeMetadata<EnumExtensionMethodsGenerationInfo>,
+                                                            IEqualityOperators<EnumExtensionMethodsGenerationInfo, EnumExtensionMethodsGenerationInfo, bool>
+{
+    private const int ExplicitFastHasFlagsMask = 0b1000;
+    private const int ExplicitFastIsDefinedMask = 0b1_0000;
+    private const int ExplicitIncludeInterfaceMask = 0b10_0000;
+    private const int ExplicitNameMask = 0b10;
+    private const int ExplicitNamespaceMask = 0b1;
+    private const int ExplicitPartialMask = 0b100;
+    private const string GeneratorAttributeFullyQualifiedName = $"{GeneratorAttributeNamespace}.{GeneratorAttributeName}";
+    private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute);
+    private const string GeneratorAttributeNamespace = Constants.Strings.AnalyzersAttributesNamespace;
+
+    /// <summary>
+    ///     Type containing the information necessary to generate code according to the declared attribute values,
+    ///     as well as the actual code to create the corresponding source code text, to be used in the
+    ///     source generator pipeline.
+    /// </summary>
+    /// <param name="enumNamespace">The fully-qualified namespace of the enum type, without assembly name.</param>
+    /// <param name="enumTypeName">
+    ///     The name of the enum type, as would be given by <see langword="nameof"/> on the enum's type
+    ///     declaration.
+    /// </param>
+    /// <param name="typeNamespace">
+    ///     The fully-qualified namespace in which to place the generated code, without assembly name. If omitted or explicitly
+    ///     null, uses the value provided in <paramref name="enumNamespace"/>.
+    /// </param>
+    /// <param name="typeName">
+    ///     The name of the generated class. If omitted or explicitly null, appends "Extensions" to the value of
+    ///     <paramref name="enumTypeName"/>.
+    /// </param>
+    /// <param name="enumBackingTypeCode">The backing type of the enum. Defaults to <see cref="int"/>.</param>
+    /// <param name="generateFastHasFlags">
+    ///     Whether to generate a fast HasFlag alternative. (Default: true) Ignored if the enum does not also have
+    ///     <see cref="FlagsAttribute"/>.
+    /// </param>
+    /// <param name="generateFastIsDefined">Whether to generate a fast IsDefined alternative. (Default: true)</param>
+    /// <remarks>
+    ///     Minimal validation is performed by this type.<br/>
+    ///     Errors in analyzed source code will result in generation failure or broken output.<br/>
+    ///     This type is not intended for use outside of Terminal.Gui library development.
+    /// </remarks>
+    public EnumExtensionMethodsGenerationInfo (
+        string enumNamespace,
+        string enumTypeName,
+        string? typeNamespace = null,
+        string? typeName = null,
+        TypeCode enumBackingTypeCode = TypeCode.Int32,
+        bool generateFastHasFlags = true,
+        bool generateFastIsDefined = true
+    ) : this (enumNamespace, enumTypeName, enumBackingTypeCode)
+    {
+        GeneratedTypeNamespace = typeNamespace ?? enumNamespace;
+        GeneratedTypeName = typeName ?? string.Concat (enumTypeName, Strings.DefaultTypeNameSuffix);
+        GenerateFastHasFlags = generateFastHasFlags;
+        GenerateFastIsDefined = generateFastIsDefined;
+    }
+
+    public EnumExtensionMethodsGenerationInfo (string enumNamespace, string enumTypeName, TypeCode enumBackingType)
+    {
+        // Interning these since they're rather unlikely to change.
+        string enumInternedNamespace = string.Intern (enumNamespace);
+        string enumInternedName = string.Intern (enumTypeName);
+        TargetTypeNamespace = enumInternedNamespace;
+        TargetTypeName = enumInternedName;
+        EnumBackingTypeCode = enumBackingType;
+    }
+
+    [AccessedThroughProperty (nameof (EnumBackingTypeCode))]
+    private TypeCode _enumBackingTypeCode;
+
+    [AccessedThroughProperty (nameof (GeneratedTypeName))]
+    private string? _generatedTypeName;
+
+    [AccessedThroughProperty (nameof (GeneratedTypeNamespace))]
+    private string? _generatedTypeNamespace;
+
+    private BitVector32 _discoveredProperties = new (0);
+
+    /// <summary>The name of the extension class.</summary>
+    public string? GeneratedTypeName
+    {
+        get => _generatedTypeName ?? string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+        set => _generatedTypeName = value ?? string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+    }
+
+    /// <summary>The namespace for the extension class.</summary>
+    /// <remarks>
+    ///     Value is not validated by the set accessor.<br/>
+    ///     Get accessor will never return null and is thus marked [NotNull] for static analysis, even though the property is
+    ///     declared as a nullable <see langword="string?"/>.<br/>If the backing field for this property is null, the get
+    ///     accessor will return <see cref="TargetTypeNamespace"/> instead.
+    /// </remarks>
+    public string? GeneratedTypeNamespace
+    {
+        get => _generatedTypeNamespace ?? TargetTypeNamespace;
+        set => _generatedTypeNamespace = value ?? TargetTypeNamespace;
+    }
+
+    /// <inheritdoc/>
+    public string TargetTypeFullName => string.Concat (TargetTypeNamespace, ".", TargetTypeName);
+
+    /// <inheritdoc/>
+    public Accessibility Accessibility
+    {
+        get;
+        [UsedImplicitly]
+        internal set;
+    } = Accessibility.Public;
+
+    /// <inheritdoc/>
+    public TypeKind TypeKind => TypeKind.Class;
+
+    /// <inheritdoc/>
+    public bool IsRecord => false;
+
+    /// <inheritdoc/>
+    public bool IsClass => true;
+
+    /// <inheritdoc/>
+    public bool IsStruct => false;
+
+    /// <inheritdoc/>
+    public bool IsByRefLike => false;
+
+    /// <inheritdoc/>
+    public bool IsSealed => false;
+
+    /// <inheritdoc/>
+    public bool IsAbstract => false;
+
+    /// <inheritdoc/>
+    public bool IsEnum => false;
+
+    /// <inheritdoc/>
+    public bool IsStatic => true;
+
+    /// <inheritdoc/>
+    public bool IncludeInterface { get; private set; }
+
+    public string GeneratedTypeFullName => $"{GeneratedTypeNamespace}.{GeneratedTypeName}";
+
+    /// <summary>Whether to generate the extension class as partial (Default: true)</summary>
+    public bool IsPartial => true;
+
+    /// <summary>The fully-qualified namespace of the source enum type.</summary>
+    public string TargetTypeNamespace
+    {
+        get;
+        [UsedImplicitly]
+        set;
+    }
+
+    /// <summary>The UNQUALIFIED name of the source enum type.</summary>
+    public string TargetTypeName
+    {
+        get;
+        [UsedImplicitly]
+        set;
+    }
+
+    /// <summary>
+    ///     The backing type for the enum.
+    /// </summary>
+    /// <remarks>For simplicity and formality, only System.Int32 and System.UInt32 are supported at this time.</remarks>
+    public TypeCode EnumBackingTypeCode
+    {
+        get => _enumBackingTypeCode;
+        set
+        {
+            if (value is not TypeCode.Int32 and not TypeCode.UInt32)
+            {
+                throw new NotSupportedException ("Only System.Int32 and System.UInt32 are supported at this time.");
+            }
+
+            _enumBackingTypeCode = value;
+        }
+    }
+
+    /// <summary>
+    ///     Whether a fast alternative to the built-in Enum.HasFlag method will be generated (Default: false)
+    /// </summary>
+    public bool GenerateFastHasFlags { [UsedImplicitly] get; set; }
+
+    /// <summary>Whether a switch-based IsDefined replacement will be generated (Default: true)</summary>
+    public bool GenerateFastIsDefined { [UsedImplicitly]get; set; } = true;
+
+    internal ImmutableHashSet<int>? IntMembers;
+    internal ImmutableHashSet<uint>? UIntMembers;
+
+    /// <summary>
+    ///     Fully-qualified name of the extension class
+    /// </summary>
+    internal string FullyQualifiedClassName => $"{GeneratedTypeNamespace}.{GeneratedTypeName}";
+
+    /// <summary>
+    ///     Whether a Flags was found on the enum type.
+    /// </summary>
+    internal bool HasFlagsAttribute {[UsedImplicitly] get; set; }
+
+    private static readonly SymbolDisplayFormat FullyQualifiedSymbolDisplayFormatWithoutGlobal =
+        SymbolDisplayFormat.FullyQualifiedFormat
+                           .WithGlobalNamespaceStyle (
+                                                      SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+
+    internal bool TryConfigure (INamedTypeSymbol enumSymbol, CancellationToken cancellationToken)
+    {
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
+        cts.Token.ThrowIfCancellationRequested ();
+
+        ImmutableArray<AttributeData> attributes = enumSymbol.GetAttributes ();
+
+        // This is theoretically impossible, but guarding just in case and canceling if it does happen.
+        if (attributes.Length == 0)
+        {
+            cts.Cancel (true);
+
+            return false;
+        }
+
+        // Check all attributes provided for anything interesting.
+        // Attributes can be in any order, so just check them all and adjust at the end if necessary.
+        // Note that we do not perform as strict validation on actual usage of the attribute, at this stage,
+        // because the analyzer should have already thrown errors for invalid uses like global namespace
+        // or unsupported enum underlying types.
+        foreach (AttributeData attr in attributes)
+        {
+            cts.Token.ThrowIfCancellationRequested ();
+            string? attributeFullyQualifiedName = attr.AttributeClass?.ToDisplayString (FullyQualifiedSymbolDisplayFormatWithoutGlobal);
+
+            // Skip if null or not possibly an attribute we care about
+            if (attributeFullyQualifiedName is null or not { Length: >= 5 })
+            {
+                continue;
+            }
+
+            switch (attributeFullyQualifiedName)
+            {
+                // For Flags enums
+                case Strings.DotnetNames.Attributes.Flags:
+                {
+                    HasFlagsAttribute = true;
+                }
+
+                    continue;
+
+                // For the attribute that started this whole thing
+                case GeneratorAttributeFullyQualifiedName:
+
+                {
+                    // If we can't successfully complete this method,
+                    // something is wrong enough that we may as well just stop now.
+                    if (!TryConfigure (attr, cts.Token))
+                    {
+                        if (cts.Token.CanBeCanceled)
+                        {
+                            cts.Cancel ();
+                        }
+
+                        return false;
+                    }
+                }
+
+                    continue;
+            }
+        }
+
+        // Now get the members, if we know we'll need them.
+        if (GenerateFastIsDefined || GenerateFastHasFlags)
+        {
+            if (EnumBackingTypeCode == TypeCode.Int32)
+            {
+                PopulateIntMembersHashSet (enumSymbol);
+            }
+            else if (EnumBackingTypeCode == TypeCode.UInt32)
+            {
+                PopulateUIntMembersHashSet (enumSymbol);
+            }
+        }
+
+        return true;
+    }
+
+    private void PopulateIntMembersHashSet (INamedTypeSymbol enumSymbol)
+    {
+        ImmutableArray<ISymbol> enumMembers = enumSymbol.GetMembers ();
+        IEnumerable<IFieldSymbol> fieldSymbols = enumMembers.OfType<IFieldSymbol> ();
+        IntMembers = fieldSymbols.Select (static m => m.HasConstantValue ? (int)m.ConstantValue : 0).ToImmutableHashSet ();
+    }
+    private void PopulateUIntMembersHashSet (INamedTypeSymbol enumSymbol)
+    {
+        UIntMembers = enumSymbol.GetMembers ().OfType<IFieldSymbol> ().Select (static m => (uint)m.ConstantValue).ToImmutableHashSet ();
+    }
+
+    private bool HasExplicitFastHasFlags
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitFastHasFlagsMask];
+        set => _discoveredProperties [ExplicitFastHasFlagsMask] = value;
+    }
+
+    private bool HasExplicitFastIsDefined
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitFastIsDefinedMask];
+        set => _discoveredProperties [ExplicitFastIsDefinedMask] = value;
+    }
+
+    private bool HasExplicitIncludeInterface
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitIncludeInterfaceMask];
+        set => _discoveredProperties [ExplicitIncludeInterfaceMask] = value;
+    }
+
+    private bool HasExplicitPartial
+    {
+        [UsedImplicitly]get => _discoveredProperties [ExplicitPartialMask];
+        set => _discoveredProperties [ExplicitPartialMask] = value;
+    }
+
+    private bool HasExplicitTypeName
+    {
+        get => _discoveredProperties [ExplicitNameMask];
+        set => _discoveredProperties [ExplicitNameMask] = value;
+    }
+
+    private bool HasExplicitTypeNamespace
+    {
+        get => _discoveredProperties [ExplicitNamespaceMask];
+        set => _discoveredProperties [ExplicitNamespaceMask] = value;
+    }
+
+    [MemberNotNullWhen (true, nameof (_generatedTypeName), nameof (_generatedTypeNamespace))]
+    private bool TryConfigure (AttributeData attr, CancellationToken cancellationToken)
+    {
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
+        cts.Token.ThrowIfCancellationRequested ();
+
+        if (attr is not { NamedArguments.Length: > 0 })
+        {
+            // Just a naked attribute, so configure with appropriate defaults.
+            GeneratedTypeNamespace = TargetTypeNamespace;
+            GeneratedTypeName = string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+
+            return true;
+        }
+
+        cts.Token.ThrowIfCancellationRequested ();
+
+        foreach (KeyValuePair<string, TypedConstant> kvp in attr.NamedArguments)
+        {
+            string? propName = kvp.Key;
+            TypedConstant propValue = kvp.Value;
+
+            cts.Token.ThrowIfCancellationRequested ();
+
+            // For every property name and value pair, set associated metadata
+            // property, if understood.
+            switch (propName, propValue)
+            {
+                // Null or empty string doesn't make sense, so skip if it happens.
+                case (null, _):
+                case ("", _):
+                    continue;
+
+                // ClassName is specified, not explicitly null, and at least 1 character long.
+                case (AttributeProperties.TypeNamePropertyName, { IsNull: false, Value: string { Length: > 1 } classNameProvidedValue }):
+                    if (string.IsNullOrWhiteSpace (classNameProvidedValue))
+                    {
+                        return false;
+                    }
+
+                    GeneratedTypeName = classNameProvidedValue;
+                    HasExplicitTypeName = true;
+
+                    continue;
+
+                // Class namespace is specified, not explicitly null, and at least 1 character long.
+                case (AttributeProperties.TypeNamespacePropertyName, { IsNull: false, Value: string { Length: > 1 } classNamespaceProvidedValue }):
+
+                    if (string.IsNullOrWhiteSpace (classNamespaceProvidedValue))
+                    {
+                        return false;
+                    }
+
+                    GeneratedTypeNamespace = classNamespaceProvidedValue;
+                    HasExplicitTypeNamespace = true;
+
+                    continue;
+
+                // FastHasFlags is specified
+                case (AttributeProperties.FastHasFlagsPropertyName, { IsNull: false } fastHasFlagsConstant):
+                    GenerateFastHasFlags = fastHasFlagsConstant.Value is true;
+                    HasExplicitFastHasFlags = true;
+
+                    continue;
+
+                // FastIsDefined is specified
+                case (AttributeProperties.FastIsDefinedPropertyName, { IsNull: false } fastIsDefinedConstant):
+                    GenerateFastIsDefined = fastIsDefinedConstant.Value is true;
+                    HasExplicitFastIsDefined = true;
+
+                    continue;
+            }
+        }
+
+        // The rest is simple enough it's not really worth worrying about cancellation, so don't bother from here on...
+
+        // Configure anything that wasn't specified that doesn't have an implicitly safe default
+        if (!HasExplicitTypeName || _generatedTypeName is null)
+        {
+            _generatedTypeName = string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix);
+        }
+
+        if (!HasExplicitTypeNamespace || _generatedTypeNamespace is null)
+        {
+            _generatedTypeNamespace = TargetTypeNamespace;
+        }
+
+        if (!HasFlagsAttribute)
+        {
+            GenerateFastHasFlags = false;
+        }
+
+        return true;
+    }
+
+    private static class AttributeProperties
+    {
+        internal const string FastHasFlagsPropertyName = nameof (GenerateEnumExtensionMethodsAttribute.FastHasFlags);
+        internal const string FastIsDefinedPropertyName = nameof (GenerateEnumExtensionMethodsAttribute.FastIsDefined);
+        internal const string TypeNamePropertyName = nameof (GenerateEnumExtensionMethodsAttribute.ClassName);
+        internal const string TypeNamespacePropertyName = nameof (GenerateEnumExtensionMethodsAttribute.ClassNamespace);
+    }
+}

+ 452 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs

@@ -0,0 +1,452 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+///     Incremental code generator for enums decorated with <see cref="GenerateEnumExtensionMethodsAttribute"/>.
+/// </summary>
+[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")]
+[Generator (LanguageNames.CSharp)]
+public sealed class EnumExtensionMethodsIncrementalGenerator : IIncrementalGenerator
+{
+    private const string ExtensionsForEnumTypeAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{ExtensionsForEnumTypeAttributeName}";
+    private const string ExtensionsForEnumTypeAttributeName = "ExtensionsForEnumTypeAttribute";
+    private const string GeneratorAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{GeneratorAttributeName}";
+    private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute);
+
+    /// <summary>Fully-qualified symbol name format without the "global::" prefix.</summary>
+    private static readonly SymbolDisplayFormat FullyQualifiedSymbolDisplayFormatWithoutGlobal =
+        SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle (SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+    /// <inheritdoc/>
+    /// <remarks>
+    ///     <para>
+    ///         Basically, this method is called once by the compiler, and is responsible for wiring up
+    ///         everything important about how source generation works.
+    ///     </para>
+    ///     <para>
+    ///         See in-line comments for specifics of what's going on.
+    ///     </para>
+    ///     <para>
+    ///         Note that <paramref name="context"/> is everything in the compilation,
+    ///         except for code generated by this generator or generators which have not yet executed.<br/>
+    ///         The methods registered to perform generation get called on-demand by the host (the IDE,
+    ///         compiler, etc), sometimes as often as every single keystroke.
+    ///     </para>
+    /// </remarks>
+    public void Initialize (IncrementalGeneratorInitializationContext context)
+    {
+        // Write out namespaces that may be used later. Harmless to declare them now and will avoid
+        // additional processing and potential omissions later on.
+        context.RegisterPostInitializationOutput (GenerateDummyNamespaces);
+
+        // This executes the delegate given to it immediately after Roslyn gets all set up.
+        // 
+        // As written, this will result in the GenerateEnumExtensionMethodsAttribute code
+        // being added to the environment, so that it can be used without having to actually
+        // be declared explicitly in the target project.
+        // This is important, as it guarantees the type will exist and also guarantees it is
+        // defined exactly as the generator expects it to be defined.
+        context.RegisterPostInitializationOutput (GenerateAttributeSources);
+
+        // Next up, we define our pipeline.
+        // To do so, we create one or more IncrementalValuesProvider<T> objects, each of which
+        // defines on stage of analysis or generation as needed.
+        //
+        // Critically, these must be as fast and efficient as reasonably possible because,
+        // once the pipeline is registered, this stuff can get called A LOT.
+        //
+        // Note that declaring these doesn't really do much of anything unless they are given to the
+        // RegisterSourceOutput method at the end of this method.
+        //
+        // The delegates are not actually evaluated right here. That is triggered by changes being
+        // made to the source code.
+
+        // This provider grabs attributes that pass our filter and then creates lightweight
+        // metadata objects to be used in the final code generation step.
+        // It also preemptively removes any nulls from the collection before handing things off
+        // to the code generation logic.
+        IncrementalValuesProvider<EnumExtensionMethodsGenerationInfo?> enumGenerationInfos =
+            context
+                .SyntaxProvider
+
+                // This method is a highly-optimized (and highly-recommended) filter on the incoming
+                // code elements that only bothers to present code that is annotated with the specified
+                // attribute, by its fully-qualified name, as a string, which is the first parameter.
+                //
+                // Two delegates are passed to it, in the second and third parameters.
+                //
+                // The second parameter is a filter predicate taking each SyntaxNode that passes the
+                // name filter above, and then refines that result.
+                //
+                // It is critical that the filter predicate be as simple and fast as possible, as it
+                // will be called a ton, triggered by keystrokes or anything else that modifies code
+                // in or even related to (in either direction) the pre-filtered code.
+                // It should collect metadata only and not actually generate any code.
+                // It must return a boolean indicating whether the supplied SyntaxNode should be
+                // given to the transform delegate at all.
+                // 
+                // The third parameter is the "transform" delegate.
+                // That one only runs when code is changed that passed both the attribute name filter
+                // and the filter predicate in the second parameter.
+                // It will be called for everything that passes both of those, so it can still happen
+                // a lot, but should at least be pretty close.
+                // In our case, it should be 100% accurate, since we're using OUR attribute, which can
+                // only be applied to enum types in the first place.
+                //
+                // That delegate is responsible for creating some sort of lightweight data structure
+                // which can later be used to generate the actual source code for output.
+                //
+                // THIS DELEGATE DOES NOT GENERATE CODE!
+                // However, it does need to return instances of the metadata class in use that are either
+                // null or complete enough to generate meaningful code from, later on.
+                //
+                // We then filter out any that were null with the .Where call at the end, so that we don't
+                // know or care about them when it's time to generate code.
+                //
+                // While the syntax of that .Where call is the same as LINQ, that is actually a
+                // highly-optimized implementation specifically for this use.
+                .ForAttributeWithMetadataName (
+                                               GeneratorAttributeFullyQualifiedName,
+                                               IsPotentiallyInterestingDeclaration,
+                                               GatherMetadataForCodeGeneration
+                                              )
+                .WithTrackingName ("CollectEnumMetadata")
+                .Where (static eInfo => eInfo is { });
+
+        // Finally, we wire up any IncrementalValuesProvider<T> instances above to the appropriate
+        // delegate that takes the SourceProductionContext that is current at run-time and an instance of
+        // our metadata type and takes appropriate action.
+        // Typically that means generating code from that metadata and adding it to the compilation via
+        // the received context object.
+        //
+        // As with everything else , the delegate will be invoked once for each item that passed
+        // all of the filters above, so we get to write that method from the perspective of a single
+        // enum type declaration.
+
+        context.RegisterSourceOutput (enumGenerationInfos, GenerateSourceFromGenerationInfo);
+    }
+
+    private static EnumExtensionMethodsGenerationInfo? GatherMetadataForCodeGeneration (
+        GeneratorAttributeSyntaxContext context,
+        CancellationToken cancellationToken
+    )
+    {
+        var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
+        cancellationToken.ThrowIfCancellationRequested ();
+
+        // If it's not an enum symbol, we don't care.
+        // EnumUnderlyingType is null for non-enums, so this validates it's an enum declaration.
+        if (context.TargetSymbol is not INamedTypeSymbol { EnumUnderlyingType: { } } namedSymbol)
+        {
+            return null;
+        }
+
+        INamespaceSymbol? enumNamespaceSymbol = namedSymbol.ContainingNamespace;
+
+        if (enumNamespaceSymbol is null or { IsGlobalNamespace: true })
+        {
+            // Explicitly choosing not to support enums in the global namespace.
+            // The corresponding analyzer will report this.
+            return null;
+        }
+
+        string enumName = namedSymbol.Name;
+
+        string enumNamespace = enumNamespaceSymbol.ToDisplayString (FullyQualifiedSymbolDisplayFormatWithoutGlobal);
+
+        TypeCode enumTypeCode = namedSymbol.EnumUnderlyingType.Name switch
+                                {
+                                    "UInt32" => TypeCode.UInt32,
+                                    "Int32" => TypeCode.Int32,
+                                    _ => TypeCode.Empty
+                                };
+
+        EnumExtensionMethodsGenerationInfo info = new (
+                                                       enumNamespace,
+                                                       enumName,
+                                                       enumTypeCode
+                                                      );
+
+        if (!info.TryConfigure (namedSymbol, cts.Token))
+        {
+            cts.Cancel ();
+            cts.Token.ThrowIfCancellationRequested ();
+        }
+
+        return info;
+    }
+
+
+    private static void GenerateAttributeSources (IncrementalGeneratorPostInitializationContext postInitializationContext)
+    {
+        postInitializationContext
+            .AddSource (
+                        $"{nameof (IExtensionsForEnumTypeAttributes)}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           // ReSharper disable All
+                                           {{Strings.Templates.AutoGeneratedCommentBlock}}
+                                           using System;
+
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>
+                                           ///     Interface to simplify general enumeration of constructed generic types for
+                                           ///     <see cref="ExtensionsForEnumTypeAttribute{TEnum}"/>
+                                           /// </summary>
+                                           {{Strings.Templates.AttributesForGeneratedInterfaces}}
+                                           public interface IExtensionsForEnumTypeAttributes
+                                           {
+                                               System.Type EnumType { get; }
+                                           }
+
+                                           """,
+                                         Encoding.UTF8));
+
+        postInitializationContext
+            .AddSource (
+                        $"{nameof (AssemblyExtendedEnumTypeAttribute)}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           // ReSharper disable All
+                                           #nullable enable
+                                           {{Strings.Templates.AutoGeneratedCommentBlock}}
+
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>Assembly attribute declaring a known pairing of an <see langword="enum" /> type to an extension class.</summary>
+                                           /// <remarks>This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported.</remarks>
+                                           {{Strings.Templates.AttributesForGeneratedTypes}}
+                                           [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple = true)]
+                                           public sealed class {{nameof(AssemblyExtendedEnumTypeAttribute)}} : System.Attribute
+                                           {
+                                               /// <summary>Creates a new instance of <see cref="AssemblyExtendedEnumTypeAttribute" /> from the provided parameters.</summary>
+                                               /// <param name="enumType">The <see cref="System.Type" /> of an <see langword="enum" /> decorated with a <see cref="GenerateEnumExtensionMethodsAttribute" />.</param>
+                                               /// <param name="extensionClass">The <see cref="System.Type" /> of the <see langword="class" /> decorated with an <see cref="ExtensionsForEnumTypeAttribute{TEnum}" /> referring to the same type as <paramref name="enumType" />.</param>
+                                               public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass)
+                                               {
+                                                   EnumType = enumType;
+                                                   ExtensionClass = extensionClass;
+                                               }
+                                               /// <summary>An <see langword="enum" /> type that has been extended by Terminal.Gui source generators.</summary>
+                                               public System.Type EnumType { get; init; }
+                                               /// <summary>A class containing extension methods for <see cref="EnumType"/>.</summary>
+                                               public System.Type ExtensionClass { get; init; }
+                                               /// <inheritdoc />
+                                               public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}";
+                                           }
+
+                                           """,
+                                         Encoding.UTF8));
+
+        postInitializationContext
+            .AddSource (
+                        $"{GeneratorAttributeFullyQualifiedName}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           {{Strings.Templates.StandardHeader}}
+                                           
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>
+                                           ///     Used to enable source generation of a common set of extension methods for enum types.
+                                           /// </summary>
+                                           {{Strings.Templates.AttributesForGeneratedTypes}}
+                                           [System.AttributeUsageAttribute (System.AttributeTargets.Enum)]
+                                           public sealed class {{GeneratorAttributeName}} : Attribute
+                                           {
+                                               /// <summary>
+                                               ///     The name of the generated static class.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".<br/>
+                                               ///     No other validation is performed, so illegal values will simply result in compiler errors.
+                                               ///     <para>
+                                               ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public string? ClassName { get; set; }
+                                           
+                                               /// <summary>
+                                               ///     The namespace in which to place the generated static class containing the extension methods.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.<br/>
+                                               ///     No other validation is performed, so illegal values will simply result in compiler errors.
+                                               ///     <para>
+                                               ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public string? ClassNamespace { get; set; }
+                                           
+                                               /// <summary>
+                                               ///     Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in
+                                               ///     <see cref="Enum.HasFlag"/> method.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     <para>
+                                               ///         Default: false
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         If the enum is not decorated with <see cref="Flags"/>, this option has no effect.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+                                               ///         with the same value will be skipped.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling
+                                               ///         avoidance of implicit or explicit cast overhead.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         Explicitly specifying a default value is unnecessary and will result in unnecessary processing.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public bool FastHasFlags { get; set; }
+                                           
+                                               /// <summary>
+                                               ///     Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in
+                                               ///     <see cref="Enum.IsDefined"/> method,
+                                               ///     using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members.
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     <para>
+                                               ///         Default: true
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         If multiple members have the same value, the first member with that value will be used and subsequent members
+                                               ///         with the same value will be skipped.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         As with <see cref="Enum.IsDefined"/> the source generator only considers explicitly-named members.<br/>
+                                               ///         Generation of values which represent valid bitwise combinations of members of enums decorated with
+                                               ///         <see cref="Flags"/> is not affected by this property.
+                                               ///     </para>
+                                               /// </remarks>
+                                               public bool FastIsDefined { get; init; } = true;
+                                           
+                                               /// <summary>
+                                               ///     Gets a <see langword="bool"/> value indicating if this <see cref="GenerateEnumExtensionMethodsAttribute"/> instance
+                                               ///     contains default values only. See <see href="#remarks">remarks</see> of this method or documentation on properties of this type for details.
+                                               /// </summary>
+                                               /// <returns>
+                                               ///     A <see langword="bool"/> value indicating if all property values are default for this
+                                               ///     <see cref="GenerateEnumExtensionMethodsAttribute"/> instance.
+                                               /// </returns>
+                                               /// <remarks>
+                                               ///     Default values that will result in a <see langword="true"/> return value are:<br/>
+                                               ///     <see cref="FastIsDefined"/> &amp;&amp; !<see cref="FastHasFlags"/> &amp;&amp; <see cref="ClassName"/>
+                                               ///     <see langword="is"/> <see langword="null"/> &amp;&amp; <see cref="ClassNamespace"/> <see langword="is"/>
+                                               ///     <see langword="null"/>
+                                               /// </remarks>
+                                               public override bool IsDefaultAttribute ()
+                                               {
+                                                   return FastIsDefined
+                                                          && !FastHasFlags
+                                                          && ClassName is null
+                                                          && ClassNamespace is null;
+                                               }
+                                           }
+                                           
+                                           """,
+                                         Encoding.UTF8));
+
+        postInitializationContext
+            .AddSource (
+                        $"{ExtensionsForEnumTypeAttributeFullyQualifiedName}.g.cs",
+                        SourceText.From (
+                                         $$"""
+                                           // ReSharper disable RedundantNameQualifier
+                                           // ReSharper disable RedundantNullableDirective
+                                           // ReSharper disable UnusedType.Global
+                                           {{Strings.Templates.AutoGeneratedCommentBlock}}
+                                           #nullable enable
+
+                                           namespace {{Strings.AnalyzersAttributesNamespace}};
+
+                                           /// <summary>
+                                           ///     Attribute written by the source generator for enum extension classes, for easier analysis and reflection.
+                                           /// </summary>
+                                           /// <remarks>
+                                           ///     Properties are just convenient shortcuts to properties of <typeparamref name="TEnum"/>.
+                                           /// </remarks>
+                                           {{Strings.Templates.AttributesForGeneratedTypes}}
+                                           [System.AttributeUsageAttribute (System.AttributeTargets.Class | System.AttributeTargets.Interface)]
+                                           public sealed class {{ExtensionsForEnumTypeAttributeName}}<TEnum>: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, Enum
+                                           {
+                                               /// <summary>
+                                               ///     The namespace-qualified name of <typeparamref name="TEnum"/>.
+                                               /// </summary>
+                                               public string EnumFullName => EnumType.FullName!;
+                                           
+                                               /// <summary>
+                                               ///     The unqualified name of <typeparamref name="TEnum"/>.
+                                               /// </summary>
+                                               public string EnumName => EnumType.Name;
+                                           
+                                               /// <summary>
+                                               ///     The namespace containing <typeparamref name="TEnum"/>.
+                                               /// </summary>
+                                               public string EnumNamespace => EnumType.Namespace!;
+                                           
+                                               /// <summary>
+                                               ///     The <see cref="Type"/> given by <see langword="typeof"/>(<typeparamref name="TEnum"/>).
+                                               /// </summary>
+                                               public Type EnumType => typeof (TEnum);
+                                           }
+                                           
+                                           """,
+                                         Encoding.UTF8));
+    }
+
+    [SuppressMessage ("Roslynator", "RCS1267", Justification = "Intentionally used so that Spans are used.")]
+    private static void GenerateDummyNamespaces (IncrementalGeneratorPostInitializationContext postInitializeContext)
+    {
+        postInitializeContext.AddSource (
+                                         string.Concat (Strings.InternalAnalyzersNamespace, "Namespaces.g.cs"),
+                                         SourceText.From (Strings.Templates.DummyNamespaceDeclarations, Encoding.UTF8));
+    }
+
+    private static void GenerateSourceFromGenerationInfo (SourceProductionContext context, EnumExtensionMethodsGenerationInfo? enumInfo)
+    {
+        // Just in case we still made it this far with a null...
+        if (enumInfo is not { })
+        {
+            return;
+        }
+
+        CodeWriter writer = new (enumInfo);
+
+        context.AddSource ($"{enumInfo.FullyQualifiedClassName}.g.cs", writer.GenerateSourceText ());
+    }
+
+    /// <summary>
+    ///     Returns true if <paramref name="syntaxNode"/> is an EnumDeclarationSyntax
+    ///     whose parent is a NamespaceDeclarationSyntax, FileScopedNamespaceDeclarationSyntax, or a
+    ///     (Class|Struct)DeclarationSyntax.<br/>
+    ///     Additional filtering is performed in later stages.
+    /// </summary>
+    private static bool IsPotentiallyInterestingDeclaration (SyntaxNode syntaxNode, CancellationToken cancellationToken)
+    {
+        cancellationToken.ThrowIfCancellationRequested ();
+
+        return syntaxNode is
+               {
+                   RawKind: 8858, //(int)SyntaxKind.EnumDeclaration,
+                   Parent.RawKind: 8845 //(int)SyntaxKind.FileScopedNamespaceDeclaration
+                                   or 8842 //(int)SyntaxKind.NamespaceDeclaration
+                                   or 8855 //(int)SyntaxKind.ClassDeclaration
+                                   or 8856 //(int)SyntaxKind.StructDeclaration
+                                   or 9068 //(int)SyntaxKind.RecordStructDeclaration
+                                   or 9063 //(int)SyntaxKind.RecordDeclaration
+               };
+    }
+}

+ 130 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumMemberCombinationsGenerator.cs

@@ -0,0 +1,130 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Terminal.Gui.Analyzers.Internal.Attributes;
+using Terminal.Gui.Analyzers.Internal.Constants;
+
+namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions;
+
+/// <summary>
+/// Implementation of <see cref="IIncrementalGenerator"/> for types decorated with <see cref="GenerateEnumMemberCombinationsAttribute"/>.
+/// </summary>
+[Generator]
+internal sealed class EnumMemberCombinationsGenerator : IIncrementalGenerator
+{
+    private const string AttributeCodeText = $$"""
+                                               {{Strings.Templates.StandardHeader}}
+                                               
+                                               namespace {{Strings.AnalyzersAttributesNamespace}};
+                                               
+                                               /// <summary>
+                                               ///     Designates an enum member for inclusion in generation of bitwise combinations with other members decorated with
+                                               ///     this attribute which have the same <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.GroupTag)}}"/> value.<br/>
+                                               /// </summary>
+                                               /// <remarks>
+                                               ///     <para>
+                                               ///         This attribute is only considered for enum types with the <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute)}}"/>.
+                                               ///     </para>
+                                               ///     <para>
+                                               ///         Masks with more than 8 bits set will
+                                               ///     </para>
+                                               /// </remarks>
+                                               [AttributeUsageAttribute(AttributeTargets.Enum)]
+                                               internal sealed class {{nameof (GenerateEnumMemberCombinationsAttribute)}} : System.Attribute
+                                               {
+                                                   public const byte MaximumPopCountLimit = 14;
+                                                   private uint _mask;
+                                                   private uint _maskPopCount;
+                                                   private byte _popCountLimit = 8;
+                                                   public required string GroupTag { get; set; }
+                                               
+                                                   public required uint Mask
+                                                   {
+                                                       get => _mask;
+                                                       set
+                                                       {
+                                                           _maskPopCount = uint.PopCount(value);
+                                               
+                                                           PopCountLimitExceeded = _maskPopCount > PopCountLimit;
+                                                           MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+                                               
+                                                           if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+                                                           {
+                                                               return;
+                                                           }
+                                               
+                                                           _mask = value;
+                                                       }
+                                                   }
+                                               
+                                                   /// <summary>
+                                                   ///     The maximum number of bits allowed to be set to 1 in <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>.
+                                                   /// </summary>
+                                                   /// <remarks>
+                                                   ///     <para>
+                                                   ///         Default: 8 (256 possible combinations)
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         Increasing this value is not recommended!<br/>
+                                                   ///         Decreasing this value is pointless unless you want to limit maximum possible generated combinations even
+                                                   ///         further.
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         If the result of <see cref="uint.PopCount"/>(<see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>) exceeds 2 ^ <see cref="PopCountLimit"/>, no
+                                                   ///         combinations will be generated for the members which otherwise would have been included by <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>.
+                                                   ///         Values exceeding the actual population count of <see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/> have no effect.
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         This option is set to a sane default of 8, but also has a hard-coded limit of 14 (16384 combinations), as a
+                                                   ///         protection against generation of extremely large files.
+                                                   ///     </para>
+                                                   ///     <para>
+                                                   ///         CAUTION: The maximum number of possible combinations possible is equal to 1 &lt;&lt;
+                                                   ///         <see cref="uint.PopCount"/>(<see cref="{{nameof (GenerateEnumMemberCombinationsAttribute.Mask)}}"/>).
+                                                   ///         See <see cref="MaximumPopCountLimit"/> for hard-coded limit,
+                                                   ///     </para>
+                                                   /// </remarks>
+                                                   public byte PopCountLimit
+                                                   {
+                                                       get => _popCountLimit;
+                                                       set
+                                                       {
+                                                           _maskPopCount = uint.PopCount(_mask);
+                                               
+                                                           PopCountLimitExceeded = _maskPopCount > value;
+                                                           MaximumPopCountLimitExceeded = _maskPopCount > MaximumPopCountLimit;
+                                               
+                                                           if (PopCountLimitExceeded || MaximumPopCountLimitExceeded)
+                                                           {
+                                                               return;
+                                                           }
+                                               
+                                                           _mask = value;
+                                                           _popCountLimit = value;
+                                                       }
+                                                   }
+                                               
+                                                   internal bool MaximumPopCountLimitExceeded { get; private set; }
+                                                   internal bool PopCountLimitExceeded { get; private set; }
+                                               }
+
+                                               """;
+
+    private const string AttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{AttributeName}";
+    private const string AttributeName = "GenerateEnumMemberCombinationsAttribute";
+
+    /// <inheritdoc/>
+    public void Initialize (IncrementalGeneratorInitializationContext context)
+    {
+        context.RegisterPostInitializationOutput (GenerateAttributeCode);
+
+        return;
+
+        static void GenerateAttributeCode (IncrementalGeneratorPostInitializationContext initContext)
+        {
+#pragma warning disable IDE0061 // Use expression body for local function
+            initContext.AddSource ($"{AttributeFullyQualifiedName}.g.cs", SourceText.From (AttributeCodeText, Encoding.UTF8));
+#pragma warning restore IDE0061 // Use expression body for local function
+        }
+    }
+}

+ 38 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs

@@ -0,0 +1,38 @@
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis;
+
+namespace Terminal.Gui.Analyzers.Internal;
+
+/// <summary>
+/// Interface for all generators to use for their metadata classes.
+/// </summary>
+/// <typeparam name="TSelf">The type implementing this interface.</typeparam>
+internal interface IGeneratedTypeMetadata<out TSelf> where TSelf : IGeneratedTypeMetadata<TSelf>
+{
+    [UsedImplicitly]
+    string GeneratedTypeNamespace { get; }
+    [UsedImplicitly]
+    string? GeneratedTypeName { get; }
+    [UsedImplicitly]
+    string GeneratedTypeFullName { get; }
+    [UsedImplicitly]
+    string TargetTypeNamespace { get; }
+    [UsedImplicitly]
+    string TargetTypeName { get; }
+    string TargetTypeFullName { get; }
+    [UsedImplicitly]
+    Accessibility Accessibility { get; }
+    TypeKind TypeKind { get; }
+    bool IsRecord { get; }
+    bool IsClass { get; }
+    bool IsStruct { get; }
+    [UsedImplicitly]
+    bool IsPartial { get; }
+    bool IsByRefLike { get; }
+    bool IsSealed { get; }
+    bool IsAbstract { get; }
+    bool IsEnum { get; }
+    bool IsStatic { get; }
+    [UsedImplicitly]
+    bool IncludeInterface { get; }
+}

+ 28 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs

@@ -0,0 +1,28 @@
+using System.Text;
+using JetBrains.Annotations;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Terminal.Gui.Analyzers.Internal;
+
+internal interface IStandardCSharpCodeGenerator<T> where T : IGeneratedTypeMetadata<T>
+{
+    /// <summary>
+    ///     Generates and returns the full source text corresponding to <see cref="Metadata"/>,
+    ///     in the requested <paramref name="encoding"/> or <see cref="Encoding.UTF8"/> if not provided.
+    /// </summary>
+    /// <param name="encoding">
+    ///     The <see cref="Encoding"/> of the generated source text or <see cref="Encoding.UTF8"/> if not
+    ///     provided.
+    /// </param>
+    /// <returns></returns>
+    [UsedImplicitly]
+    [SkipLocalsInit]
+    ref readonly SourceText GenerateSourceText (Encoding? encoding = null);
+
+    /// <summary>
+    ///     A type implementing <see cref="IGeneratedTypeMetadata{T}"/> which
+    ///     will be used for source generation.
+    /// </summary>
+    [UsedImplicitly]
+    T Metadata { get; set; }
+}

+ 71 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/IndentedTextWriterExtensions.cs

@@ -0,0 +1,71 @@
+using System.CodeDom.Compiler;
+
+namespace Terminal.Gui.Analyzers.Internal;
+
+/// <summary>
+///     Just a simple set of extension methods to increment and decrement the indentation
+///     level of an <see cref="IndentedTextWriter"/> via push and pop terms, and to avoid having
+///     explicit values all over the place.
+/// </summary>
+public static class IndentedTextWriterExtensions
+{
+    /// <summary>
+    ///     Decrements <see cref="IndentedTextWriter.Indent"/> by 1, but only if it is greater than 0.
+    /// </summary>
+    /// <returns>
+    ///     The resulting indentation level of the <see cref="IndentedTextWriter"/>.
+    /// </returns>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static int Pop (this IndentedTextWriter w, string endScopeDelimiter = "}")
+    {
+        if (w.Indent > 0)
+        {
+            w.Indent--;
+            w.WriteLine (endScopeDelimiter);
+        }
+        return w.Indent;
+    }
+
+    /// <summary>
+    ///     Decrements <see cref="IndentedTextWriter.Indent"/> by 1 and then writes a closing curly brace.
+    /// </summary>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static void PopCurly (this IndentedTextWriter w, bool withSemicolon = false)
+    {
+        w.Indent--;
+
+        if (withSemicolon)
+        {
+            w.WriteLine ("};");
+        }
+        else
+        {
+            w.WriteLine ('}');
+        }
+    }
+
+    /// <summary>
+    ///     Increments <see cref="IndentedTextWriter.Indent"/> by 1, with optional parameters to customize the scope push.
+    /// </summary>
+    /// <param name="w">An instance of an <see cref="IndentedTextWriter"/>.</param>
+    /// <param name="declaration">
+    ///     The first line to be written before indenting and before the optional <paramref name="scopeDelimiter"/> line or
+    ///     null if not needed.
+    /// </param>
+    /// <param name="scopeDelimiter">
+    ///     An opening delimiter to write. Written before the indentation and after <paramref name="declaration"/> (if provided). Default is an opening curly brace.
+    /// </param>
+    /// <remarks>Calling with no parameters will write an opening curly brace and a line break at the current indentation and then increment.</remarks>
+    [MethodImpl (MethodImplOptions.AggressiveInlining)]
+    public static void Push (this IndentedTextWriter w, string? declaration = null, char scopeDelimiter = '{')
+    {
+        if (declaration is { Length: > 0 })
+        {
+            w.WriteLine (declaration);
+        }
+
+        w.WriteLine (scopeDelimiter);
+
+        w.Indent++;
+    }
+}

+ 8 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Properties/launchSettings.json

@@ -0,0 +1,8 @@
+{
+  "profiles": {
+    "InternalAnalyzers Debug": {
+      "commandName": "DebugRoslynComponent",
+      "targetProject": "..\\Terminal.Gui.Analyzers.Internal.Debugging\\Terminal.Gui.Analyzers.Internal.Debugging.csproj"
+    }
+  }
+}

+ 98 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj

@@ -0,0 +1,98 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <!-- 
+        Do not remove netstandard2.0 from the TargetFrameworks.
+        Visual Studio requires that Analyzers/Generators target netstandard2.0 to work properly.
+        Additional TFMs are for support of additional APIs and language features.
+        -->
+        <TargetFramework>netstandard2.0</TargetFramework>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <OutputType>Library</OutputType>
+        <LangVersion>12</LangVersion>
+        <Nullable>enable</Nullable>
+        <RootNamespace>Terminal.Gui.Analyzers.Internal</RootNamespace>
+        <ImplicitUsings>disable</ImplicitUsings>
+        <InvariantGlobalization>true</InvariantGlobalization>
+        <EnableNETAnalyzers>true</EnableNETAnalyzers>
+        <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+        <AnalysisLevel>latest-recommended</AnalysisLevel>
+        <WarningLevel>7</WarningLevel>
+        <CharacterSet>UTF-8</CharacterSet>
+        <CodeAnalysisIgnoreGeneratedCode>true</CodeAnalysisIgnoreGeneratedCode>
+        <Deterministic>true</Deterministic>
+        <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
+        <UTF8OutPut>true</UTF8OutPut>
+        <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
+        <DefineConstants>$(DefineConstants);JETBRAINS_ANNOTATIONS;CONTRACTS_FULL;CODE_ANALYSIS</DefineConstants>
+        <GenerateDocumentationFile>True</GenerateDocumentationFile>
+        <IsRoslynComponent>true</IsRoslynComponent>
+        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <ApiCompatExcludeAttributesFile Include="ApiCompatExcludedAttributes.txt" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <Compile Remove="Compatibility/*.cs" />
+    </ItemGroup>
+
+    <Choose>
+        <When Condition="'$(TargetFramework)' == 'netstandard2.0'">
+            <PropertyGroup>
+                <!-- Disabling some useless warnings caused by the netstandard2.0 target -->
+                <NoWarn>$(NoWarn);nullable;CA1067</NoWarn>
+            </PropertyGroup>
+            <ItemGroup>                                                                         
+                <Compile Include="Compatibility/CompilerFeatureRequiredAttribute.cs" />         
+                <Compile Include="Compatibility/IsExternalInit.cs" />                           
+                <Compile Include="Compatibility/IEqualityOperators.cs" />
+                <Compile Include="Compatibility/NullableAttributes.cs" />                         
+                <Compile Include="Compatibility/RequiredMemberAttribute.cs" />                  
+                <Compile Include="Compatibility/SetsRequiredMembersAttribute.cs" />             
+            </ItemGroup>                                                                        
+            <ItemGroup>
+                <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" PrivateAssets="all" />
+                <PackageReference Include="System.Runtime.Extensions" Version="4.3.1" PrivateAssets="all" />
+                <PackageReference Include="System.Runtime.Numerics" Version="4.3.0" PrivateAssets="all" />
+            </ItemGroup>
+        </When>
+        <When Condition="'$(TargetFramework)' == 'net8.0'">
+        </When>
+    </Choose>
+    
+    <ItemGroup>
+        <Compile Include="Compatibility/NumericExtensions.cs" />
+        <Compile Include="Compatibility/SkipLocalsInitAttribute.cs" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <AdditionalFiles Include="AnalyzerReleases.Unshipped.md" />
+        <AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.CodeAnalysis" Version="4.9.2" PrivateAssets="all" />
+        <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
+        <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" PrivateAssets="all" />
+        <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" PrivateAssets="all" />
+        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" PrivateAssets="all" />
+        <PackageReference Include="Roslynator.Analyzers" Version="4.12.1" PrivateAssets="all">
+            <!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
+        </PackageReference>
+        <PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.1" PrivateAssets="all">
+            <!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
+        </PackageReference>
+        <PackageReference Include="Roslynator.CSharp" Version="4.12.1" PrivateAssets="all" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <Using Include="System.Buffers" />
+        <Using Include="System.Collections.Specialized" />
+        <Using Include="System.Numerics" />
+        <Using Include="System.Runtime.CompilerServices" />
+    </ItemGroup>
+
+</Project>

+ 4 - 0
Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj.DotSettings

@@ -0,0 +1,4 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp120</s:String>
+	<s:String x:Key="/Default/CodeInspection/Highlighting/UsageCheckingInspectionLevel/@EntryValue">InternalsOnly</s:String>
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=compatibility/@EntryIndexedValue">False</s:Boolean></wpf:ResourceDictionary>

+ 6 - 0
Directory.build.props

@@ -0,0 +1,6 @@
+<Project>
+  <ItemGroup>
+    <PackageReference Include="JetBrains.Annotations" Version="2023.3.0" PrivateAssets="all" />
+    <PackageReference Include="JetBrains.ExternalAnnotations" Version="10.2.147" PrivateAssets="all" />
+  </ItemGroup>
+</Project>

+ 16 - 0
Scripts/COPYRIGHT

@@ -0,0 +1,16 @@
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The MIT License (MIT)
+// Copyright © 2024 Brandon Thetford (@dodexahedron)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ 46 - 0
Scripts/README.md

@@ -0,0 +1,46 @@
+## Development and Design-Time PowerShell Modules
+This directory contains PowerShell modules for use when working with Terminal.sln
+
+### Purpose
+These modules will be modifed and extended as time goes on, whenever someone decides to add something to make life easier.
+
+### Requirements
+These modules are designed for **PowerShell Core, version 7.4 or higher**, on any platform, and must be run directly within a pwsh process.\
+If you want to use them from within another application, such as PowerShell hosted inside VSCode, you must first run `pwsh` in that terminal.
+
+As the primary development environment for Terminal.Gui is Visual Studio 2022+, some functionality may be limited, unavailable, or not work on platforms other than Windows.\
+Most should still work on Linux, however.\
+Functions which are platform-specific will be documented as such in their Get-Help documentation.
+
+Specific requirements for each module can be found in the module manifests and will be automatically imported or, if unavailable, PowerShell will tell you what's missing.
+
+### Usage
+From a PowerShell 7.4 or higher prompt, navigate to your Terminal.Gui repository directory, and then into the Scripts directory (the same directory as this document).
+
+#### Import Module and Configure Environment
+Run the following command to import all Terminal.Gui.PowerShell.* modules:
+```powershell
+Import-Module ./Terminal.Gui.PowerShell.psd1
+```
+If the environment meets the requirements, the modules will now be loaded into the current powershell session and exported commands will be immediately available for use.
+
+#### Getting Help
+All exported functions and commandlets are provided with full PowerShell help annotations compatible with `Get-Help`.
+
+See [The Get-Help documentation at Microsoft Learn]([https://](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-help?view=powershell-7.4)) for Get-Help information.
+
+#### Cleaning Up/Resetting Environment
+No environment changes made by the modules on import are persistent.
+
+When you are finished using the modules, you can optionally unload the modules, which will also reset the configuration changes made on import, by simply exiting the PowerShell session (`exit`) or by running the following command:\
+**NOTE DIFFERENT TEXT FROM IMPORT COMMAND!**
+```powershell
+Remove-Module Terminal.Gui.PowerShell
+```
+
+### LICENSE
+MIT License
+
+Original Author: Brandon Thetford (@dodexahedron)
+
+See COPYRIGHT in this directory for license text.

+ 105 - 0
Scripts/Terminal.Gui.PowerShell.Analyzers.psm1

@@ -0,0 +1,105 @@
+<#
+  .SYNOPSIS
+  Builds all analyzer projects in Debug and Release configurations.
+  .DESCRIPTION
+  Uses dotnet build to build all analyzer projects, with optional behavior changes via switch parameters.
+  .PARAMETER AutoClose
+  Automatically close running Visual Studio processes which have the Terminal.sln solution loaded, before taking any other actions.
+  .PARAMETER AutoLaunch
+  Automatically start a new Visual Studio process and load the solution after completion.
+  .PARAMETER Force
+  Carry out operations unconditionally and do not prompt for confirmation.
+  .PARAMETER NoClean
+  Do not delete the bin and obj folders before building the analyzers. Usually best not to use this, but can speed up the builds slightly.
+  .PARAMETER Quiet
+  Write less text output to the terminal.
+  .INPUTS
+  None
+  .OUTPUTS
+  None
+#>
+Function Build-Analyzers {
+  [CmdletBinding()]
+  param(
+    [Parameter(Mandatory=$false, HelpMessage="Automatically close running Visual Studio processes which have the Terminal.sln solution loaded, before taking any other actions.")]
+    [switch]$AutoClose,
+    [Parameter(Mandatory=$false, HelpMessage="Automatically start a new Visual Studio process and load the solution after completion.")]
+    [switch]$AutoLaunch,
+    [Parameter(Mandatory=$false, HelpMessage="Carry out operations unconditionally and do not prompt for confirmation.")]
+    [switch]$Force,
+    [Parameter(Mandatory=$false, HelpMessage="Do not delete the bin and obj folders before building the analyzers.")]
+    [switch]$NoClean,
+    [Parameter(Mandatory=$false, HelpMessage="Write less text output to the terminal.")]
+    [switch]$Quiet
+  )
+  
+  if($AutoClose) {
+    if(!$Quiet) {
+      Write-Host Closing Visual Studio processes
+    }
+    Close-Solution
+  }
+
+  if($Force){
+    $response = 'Y'
+  }
+  elseif(!$Force && $NoClean){
+    $response = ($r = Read-Host "Pre-build Terminal.Gui.InternalAnalyzers without removing old build artifacts? [Y/n]") ? $r : 'Y'
+  }
+  else{
+    $response = ($r = Read-Host "Delete bin and obj folders for Terminal.Gui and Terminal.Gui.InternalAnalyzers and pre-build Terminal.Gui.InternalAnalyzers? [Y/n]") ? $r : 'Y'
+  }
+
+  if (($response -ne 'Y')) {
+    Write-Host Took no action
+    return
+  }
+  
+  New-Variable -Name solutionRoot -Visibility Public -Value (Resolve-Path ..)
+  Push-Location $solutionRoot
+  New-Variable -Name solutionFile -Visibility Public -Value (Resolve-Path ./Terminal.sln)
+  $mainProjectRoot = Resolve-Path ./Terminal.Gui
+  $mainProjectFile = Join-Path $mainProjectRoot Terminal.Gui.csproj
+  $analyzersRoot = Resolve-Path ./Analyzers
+  $internalAnalyzersProjectRoot = Join-Path $analyzersRoot Terminal.Gui.Analyzers.Internal
+  $internalAnalyzersProjectFile = Join-Path $internalAnalyzersProjectRoot Terminal.Gui.Analyzers.Internal.csproj
+  
+  if(!$NoClean) {
+    if(!$Quiet) {
+      Write-Host Deleting bin and obj folders for Terminal.Gui
+    }
+    if(Test-Path $mainProjectRoot/bin) {
+      Remove-Item -Recurse -Force $mainProjectRoot/bin
+      Remove-Item -Recurse -Force $mainProjectRoot/obj
+    }
+
+    if(!$Quiet) {
+      Write-Host Deleting bin and obj folders for Terminal.Gui.InternalAnalyzers
+    }
+    if(Test-Path $internalAnalyzersProjectRoot/bin) {
+      Remove-Item -Recurse -Force $internalAnalyzersProjectRoot/bin
+      Remove-Item -Recurse -Force $internalAnalyzersProjectRoot/obj
+    }
+  }
+  
+  if(!$Quiet) {
+    Write-Host Building analyzers in Debug configuration
+  }
+  dotnet build $internalAnalyzersProjectFile --no-incremental --nologo --force --configuration Debug
+
+  if(!$Quiet) {
+    Write-Host Building analyzers in Release configuration
+  }
+  dotnet build $internalAnalyzersProjectFile --no-incremental --nologo --force --configuration Release
+
+  if(!$AutoLaunch) {
+    Write-Host -ForegroundColor Green Finished. Restart Visual Studio for changes to take effect.
+  } else {
+    if(!$Quiet) {
+      Write-Host -ForegroundColor Green Finished. Re-loading Terminal.sln.
+    }
+    Open-Solution
+  }
+
+  return
+}

+ 131 - 0
Scripts/Terminal.Gui.PowerShell.Build.psd1

@@ -0,0 +1,131 @@
+@{
+
+# No root module because this is a manifest module.
+RootModule = ''
+
+# Version number of this module.
+ModuleVersion = '1.0.0'
+
+# Supported PSEditions
+CompatiblePSEditions = @('Core')
+
+# ID used to uniquely identify this module
+GUID = 'c4a1de77-83fb-45a3-b1b5-18d275ef3601'
+
+# Author of this module
+Author = 'Brandon Thetford (GitHub @dodexahedron)'
+
+# Company or vendor of this module
+CompanyName = 'The Terminal.Gui Project'
+
+# Copyright statement for this module
+Copyright = 'Brandon Thetford (GitHub @dodexahedron), provided to the Terminal.Gui project and you under the MIT license'
+
+# Description of the functionality provided by this module
+Description = 'Build helper functions for Terminal.Gui.'
+
+# Minimum version of the PowerShell engine required by this module
+PowerShellVersion = '7.4.0'
+
+# Name of the PowerShell "host" subsystem (not system host name). Helps ensure that we know what to expect from the environment.
+PowerShellHostName = 'ConsoleHost'
+
+# Minimum version of the PowerShell host required by this module
+PowerShellHostVersion = '7.4.0'
+
+# Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
+# Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
+# Has nothing to do with runtime use of Terminal.Gui.
+ProcessorArchitecture = 'Amd64'
+
+# Modules that must be imported into the global environment prior to importing this module
+RequiredModules = @(
+    @{
+        ModuleName='Microsoft.PowerShell.Utility'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='Microsoft.PowerShell.Management'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='PSReadLine'
+        ModuleVersion='2.3.4'
+    },
+    "./Terminal.Gui.PowerShell.Core.psd1"
+)
+
+# Assemblies that must be loaded prior to importing this module
+# RequiredAssemblies = @()
+
+# Script files (.ps1) that are run in the caller's environment prior to importing this module.
+# ScriptsToProcess = @()
+
+# Type files (.ps1xml) to be loaded when importing this module
+# TypesToProcess = @()
+
+# Format files (.ps1xml) to be loaded when importing this module
+# FormatsToProcess = @()
+
+# Modules to import as nested modules.
+NestedModules = @('./Terminal.Gui.PowerShell.Build.psm1')
+
+# Functions to export from this module.
+FunctionsToExport = @('Build-TerminalGui')
+
+# Cmdlets to export from this module.
+CmdletsToExport = @()
+
+# Variables to export from this module
+VariablesToExport = @()
+
+# Aliases to export from this module.
+AliasesToExport = @()
+
+# List of all modules packaged with this module
+# ModuleList = @()
+
+# List of all files packaged with this module
+# FileList = @()
+
+# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+PrivateData = @{
+
+    PSData = @{
+
+        # Tags applied to this module. These help with module discovery in online galleries.
+        # Tags = @()
+
+        # A URL to the license for this module.
+        LicenseUri = 'https://github.com/gui-cs/Terminal.Gui/tree/v2_develop/Scripts/COPYRIGHT'
+
+        # A URL to the main website for this project.
+        ProjectUri = 'https://github.com/gui-cs/Terminal.Gui'
+
+        # A URL to an icon representing this module.
+        # IconUri = ''
+
+        # ReleaseNotes of this module
+        ReleaseNotes = 'See change history and releases for Terminal.Gui on GitHub'
+
+        # Prerelease string of this module
+        # Prerelease = ''
+
+        # Flag to indicate whether the module requires explicit user acceptance for install/update/save
+        RequireLicenseAcceptance = $false
+
+        # External dependent modules of this module
+        # ExternalModuleDependencies = @()
+
+    } # End of PSData hashtable
+
+} # End of PrivateData hashtable
+
+# HelpInfo URI of this module
+# HelpInfoURI = ''
+
+# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+# DefaultCommandPrefix = ''
+
+}
+

+ 32 - 0
Scripts/Terminal.Gui.PowerShell.Build.psm1

@@ -0,0 +1,32 @@
+<#
+  .SYNOPSIS
+  Builds the Terminal.Gui library.
+  .DESCRIPTION
+  Builds the Terminal.Gui library.
+  Optional parameter sets are available to customize the build.
+  .PARAMETER versionBase
+  The base version for the Terminal.Gui library.
+#>
+Function Build-TerminalGui {
+  [CmdletBinding(SupportsShouldProcess, PositionalBinding=$false, DefaultParameterSetName="Basic", ConfirmImpact="Medium")]
+  [OutputType([bool],[PSObject])]
+  param(
+      [Parameter(Mandatory=$true)]
+      [Version]$versionBase,
+      [Parameter(Mandatory=$true, ParameterSetName="Custom")]
+      [switch]$Custom,
+      [Parameter(Mandatory=$false, ParameterSetName="Custom")]
+      [ValidateSet("Debug", "Release")]
+      [string]$slnBuildConfiguration = "Release",
+      [Parameter(Mandatory=$false, ParameterSetName="Custom")]
+      [ValidateSet("Any CPU", "x86"<#, "x64" #>)]
+      [string]$slnBuildPlatform = "Any CPU"
+  )
+
+  if(!$PSCmdlet.ShouldProcess("Building in $slnBuildConfiguration configuration for $slnBuildPlatform", "Terminal.Gui", "BUILDING")) {
+    return $null
+  }
+
+  Write-Host NOT IMPLEMENTED. No Action has been taken.
+  return $false
+}

+ 138 - 0
Scripts/Terminal.Gui.PowerShell.Core.psd1

@@ -0,0 +1,138 @@
+#
+# Module manifest for module 'Terminal.Gui.PowerShell'
+#
+# Generated by: Brandon Thetford (GitHub @dodexahedron)
+#
+# Generated on: 4/19/2024
+#
+
+@{
+
+# No root module because this is a manifest module.
+RootModule = ''
+
+# Version number of this module.
+ModuleVersion = '1.0.0'
+
+# Supported PSEditions
+CompatiblePSEditions = @('Core')
+
+# ID used to uniquely identify this module
+GUID = 'c661fb12-70ae-4a9e-a95c-786a7980681d'
+
+# Author of this module
+Author = 'Brandon Thetford (GitHub @dodexahedron)'
+
+# Company or vendor of this module
+CompanyName = 'The Terminal.Gui Project'
+
+# Copyright statement for this module
+Copyright = 'Brandon Thetford (GitHub @dodexahedron), provided to the Terminal.Gui project and you under the MIT license'
+
+# Description of the functionality provided by this module
+Description = 'Utilities for development-time operations on and management of components of Terminal.Gui code and other assets.'
+
+# Minimum version of the PowerShell engine required by this module
+PowerShellVersion = '7.4.0'
+
+# Name of the PowerShell "host" subsystem (not system host name). Helps ensure that we know what to expect from the environment.
+PowerShellHostName = 'ConsoleHost'
+
+# Minimum version of the PowerShell host required by this module
+PowerShellHostVersion = '7.4.0'
+
+# Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
+# Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
+# Has nothing to do with runtime use of Terminal.Gui.
+ProcessorArchitecture = 'Amd64'
+
+# Modules that must be imported into the global environment prior to importing this module
+RequiredModules = @(
+    @{
+        ModuleName='Microsoft.PowerShell.Utility'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='Microsoft.PowerShell.Management'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='PSReadLine'
+        ModuleVersion='2.3.4'
+    }
+)
+
+# Assemblies that must be loaded prior to importing this module
+# RequiredAssemblies = @()
+
+# Script files (.ps1) that are run in the caller's environment prior to importing this module.
+# ScriptsToProcess = @()
+
+# Type files (.ps1xml) to be loaded when importing this module
+# TypesToProcess = @()
+
+# Format files (.ps1xml) to be loaded when importing this module
+# FormatsToProcess = @()
+
+# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
+NestedModules = @('./Terminal.Gui.PowerShell.Core.psm1')
+
+# Functions to export from this module.
+FunctionsToExport = @('Open-Solution','Close-Solution')
+
+# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
+CmdletsToExport = @()
+
+# Variables to export from this module
+VariablesToExport = @()
+
+# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
+AliasesToExport = @()
+
+# List of all modules packaged with this module
+# ModuleList = @()
+
+# List of all files packaged with this module
+# FileList = @()
+
+# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+PrivateData = @{
+
+    PSData = @{
+
+        # Tags applied to this module. These help with module discovery in online galleries.
+        # Tags = @()
+
+        # A URL to the license for this module.
+        LicenseUri = 'https://github.com/gui-cs/Terminal.Gui/tree/v2_develop/Scripts/COPYRIGHT'
+
+        # A URL to the main website for this project.
+        ProjectUri = 'https://github.com/gui-cs/Terminal.Gui'
+
+        # A URL to an icon representing this module.
+        # IconUri = ''
+
+        # ReleaseNotes of this module
+        ReleaseNotes = 'See change history and releases for Terminal.Gui on GitHub'
+
+        # Prerelease string of this module
+        # Prerelease = ''
+
+        # Flag to indicate whether the module requires explicit user acceptance for install/update/save
+        RequireLicenseAcceptance = $false
+
+        # External dependent modules of this module
+        # ExternalModuleDependencies = @()
+
+    } # End of PSData hashtable
+
+} # End of PrivateData hashtable
+
+# HelpInfo URI of this module
+# HelpInfoURI = ''
+
+# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+# DefaultCommandPrefix = ''
+
+}
+

+ 143 - 0
Scripts/Terminal.Gui.PowerShell.Core.psm1

@@ -0,0 +1,143 @@
+<#
+  .SYNOPSIS
+  (Windows Only) Opens Visual Studio and loads Terminal.sln.
+  .DESCRIPTION
+  (Windows Only) Opens Visual Studio and loads Terminal.sln.
+  .PARAMETER SolutionFilePath
+  (Optional) If specified, the path to the solution file. Typically unnecessary to supply this parameter.
+  .INPUTS
+  None
+  .OUTPUTS
+  None
+#>
+Function Open-Solution {
+  [CmdletBinding()]
+  param(
+    [Parameter(Mandatory=$false, HelpMessage="The path to the solution file to open.")]
+    [Uri]$SolutionFilePath = (Resolve-Path "../Terminal.sln")
+  )
+  
+  if(!$IsWindows) {
+    [string]$warningMessage = "The Open-Solution cmdlet is only supported on Windows.`n`
+    Attempt to open file $SolutionFilePath with the system default handler?"
+    
+    Write-Warning $warningMessage -WarningAction Inquire
+  }
+  
+  Invoke-Item $SolutionFilePath
+  return
+}
+
+<#
+  .SYNOPSIS
+  (Windows Only) Closes Visual Studio processes with Terminal.sln loaded.
+  .DESCRIPTION
+  (Windows Only) Closes Visual Studio processes with Terminal.sln loaded by finding any VS processes launched with the solution file or with 'Terminal' in their main window titles.
+  .INPUTS
+  None
+  .OUTPUTS
+  None
+#>
+Function Close-Solution {
+  $vsProcesses = Get-Process -Name devenv | Where-Object { ($_.CommandLine -Match ".*Terminal\.sln.*" -or $_.MainWindowTitle -Match "Terminal.*") }
+  Stop-Process -InputObject $vsProcesses
+  Remove-Variable vsProcesses
+}
+
+<#
+  .SYNOPSIS
+  Sets up a standard environment for other Terminal.Gui.PowerShell scripts and modules.
+  .DESCRIPTION
+  Configures environment variables and global variables for other Terminal.Gui.PowerShell scripts to use.
+  Also modifies the prompt to indicate the session has been altered.
+  Reset changes by exiting the session or by calling Reset-PowerShellEnvironment or ./ResetEnvironment.ps1.
+  .PARAMETER Debug
+  Minimally supported for Write-Debug calls in this function only.
+  .NOTES
+  Mostly does not respect common parameters like WhatIf, Confirm, etc.
+  This is just meant to be called by other scripts.
+  Calling this manually is not supported.
+#>
+Function Set-PowerShellEnvironment {
+  [CmdletBinding()]
+  param()
+
+  # Set a custom prompt to indicate we're in our modified environment.
+  # Save the normal one first, though.
+  # And save it as ReadOnly and without the -Force parameter, so this will be skipped if run more than once in the same session without a reset.
+  New-Variable -Name NormalPrompt -Option ReadOnly -Scope Global -Value (Get-Item Function:prompt).ScriptBlock -ErrorAction SilentlyContinue
+  Set-Item Function:prompt { "TGPS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "; }
+
+  # Save existing PSModulePath for optional reset later.
+  # If it is already saved, do not overwrite, but continue anyway.
+  New-Variable -Name OriginalPSModulePath -Visibility Public -Option ReadOnly -Scope Global -Value ($Env:PSModulePath) -ErrorAction SilentlyContinue
+  Write-Debug -Message "`$OriginalPSModulePath is $OriginalPSModulePath" -Debug:$DebugPreference
+
+  # Get platform-specific path variable entry separator. Continue if it's already set.
+  New-Variable -Name PathVarSeparator -Visibility Public -Option ReadOnly -Scope Global -Value ";" -Description 'Separator character used in environment variables such as $Env:PSModulePath' -ErrorAction SilentlyContinue
+
+  if(!$IsWindows) {
+    $PathVarSeparator = ':'
+  }
+  Write-Debug -Message "`$PathVarSeparator is $PathVarSeparator" -Debug:$DebugPreference
+
+  # If Env:PSModulePath already has the current path, don't append it again.
+  if($Env:PSModulePath -notlike "*$((Resolve-Path .).Path)*") {
+    Write-Debug -Message "Appending $((Resolve-Path .).Path) to `$Env:PSModulePath" -Debug:$DebugPreference
+    $env:PSModulePath = Join-String -Separator $PathVarSeparator -InputObject @( $env:PSModulePath, (Resolve-Path .).Path )
+  }
+  Write-Debug -Message "`$Env:PSModulePath is $Env:PSModulePath" -Debug:$DebugPreference
+}
+
+
+<#
+  .SYNOPSIS
+  Resets changes made by ConfigureEnvironment.pst to the current PowerShell environment.
+  .DESCRIPTION
+  Optional function to undo changes to the current session made by ConfigureEnvironment.ps1.
+  Changes only affect the current session, so exiting will also "reset." 
+  .PARAMETER Exit
+  Switch parameter that, if specified, exits the current PowerShell environment.
+  Does not bother doing any other operations, as none are necessary.
+  .INPUTS
+  None
+  .OUTPUTS
+  None
+  .EXAMPLE
+  Reset-PowerShellEnvironment
+  To undo changes in the current session.
+  .EXAMPLE
+  Reset-PowerShellEnvironment -Exit
+  To exit the current session. Same as simply using the Exit command.
+#>
+Function Reset-PowerShellEnvironment {
+  [CmdletBinding(DefaultParameterSetName="Basic")]
+  param(
+    [Parameter(Mandatory=$false, ParameterSetName="Basic")]
+    [switch]$Exit
+  )
+
+  if($Exit) {
+    [Environment]::Exit(0)
+  }
+
+  if(Get-Variable -Name NormalPrompt -Scope Global -ErrorAction SilentlyContinue){
+    Set-Item Function:prompt $NormalPrompt
+    Remove-Variable -Name NormalPrompt -Scope Global -Force -ErrorAction SilentlyContinue
+  }
+
+  if(Get-Variable -Name OriginalPSModulePath -Scope Global -ErrorAction SilentlyContinue){
+    $Env:PSModulePath = $OriginalPSModulePath
+    Remove-Variable -Name OriginalPSModulePath -Scope Global -Force -ErrorAction SilentlyContinue
+  }
+
+  Remove-Variable -Name PathVarSeparator -Scope Global -Force -ErrorAction SilentlyContinue
+}
+
+# This ensures the environment is reset when unloading the module.
+# Without this, function:prompt will be undefined.
+$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { 
+  Reset-PowerShellEnvironment
+}
+
+Set-PowerShellEnvironment

+ 135 - 0
Scripts/Terminal.Gui.PowerShell.Git.psd1

@@ -0,0 +1,135 @@
+#
+# Module manifest for module 'Terminal.Gui.PowerShell.Git'
+#
+# Generated by: Brandon Thetford
+#
+# Generated on: 4/26/2024
+#
+
+@{
+
+# Script module or binary module file associated with this manifest.
+RootModule = ''
+
+# Version number of this module.
+ModuleVersion = '1.0.0'
+
+# Supported PSEditions
+CompatiblePSEditions = 'Core'
+
+# ID used to uniquely identify this module
+GUID = '33a6c4c9-c0a7-4c09-b171-1da0878f93ea'
+
+# Author of this module
+Author = 'Brandon Thetford (GitHub @dodexahedron)'
+
+# Company or vendor of this module
+CompanyName = 'The Terminal.Gui Project'
+
+# Copyright statement for this module
+Copyright = 'Brandon Thetford (GitHub @dodexahedron), provided to the Terminal.Gui project and you under the MIT license'
+
+# Description of the functionality provided by this module
+Description = 'Simple helper commands for common git operations.'
+
+# Minimum version of the PowerShell engine required by this module
+PowerShellVersion = '7.4'
+
+# Name of the PowerShell host required by this module
+PowerShellHostName = 'ConsoleHost'
+
+# Minimum version of the PowerShell host required by this module
+PowerShellHostVersion = '7.4.0'
+
+# Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
+# Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
+# Has nothing to do with runtime use of Terminal.Gui.
+ProcessorArchitecture = 'AMD64'
+
+# Modules that must be imported into the global environment prior to importing this module
+RequiredModules = @(
+    @{
+        ModuleName='Microsoft.PowerShell.Utility'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='Microsoft.PowerShell.Management'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='PSReadLine'
+        ModuleVersion='2.3.4'
+    }
+)
+
+# Script files (.ps1) that are run in the caller's environment prior to importing this module.
+ScriptsToProcess = @()
+
+# Type files (.ps1xml) to be loaded when importing this module
+TypesToProcess = @()
+
+# Format files (.ps1xml) to be loaded when importing this module
+FormatsToProcess = @()
+
+# Modules to import as nested modules.
+NestedModules = @("./Terminal.Gui.PowerShell.Git.psm1")
+
+# Functions to export from this module.
+FunctionsToExport = @('New-GitBranch')
+
+# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
+CmdletsToExport = @()
+
+# Variables to export from this module
+VariablesToExport = @()
+
+# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
+AliasesToExport = @()
+
+# DSC resources to export from this module
+DscResourcesToExport = @()
+
+# List of all modules packaged with this module
+ModuleList = @('./Terminal.Gui.PowerShell.Git.psm1')
+
+# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+PrivateData = @{
+
+    PSData = @{
+
+        # Tags applied to this module. These help with module discovery in online galleries.
+        # Tags = @()
+
+        # A URL to the license for this module.
+        # LicenseUri = ''
+
+        # A URL to the main website for this project.
+        # ProjectUri = ''
+
+        # A URL to an icon representing this module.
+        # IconUri = ''
+
+        # ReleaseNotes of this module
+        # ReleaseNotes = ''
+
+        # Prerelease string of this module
+        # Prerelease = ''
+
+        # Flag to indicate whether the module requires explicit user acceptance for install/update/save
+        # RequireLicenseAcceptance = $false
+
+        # External dependent modules of this module
+        # ExternalModuleDependencies = @()
+
+    } # End of PSData hashtable
+
+} # End of PrivateData hashtable
+
+# HelpInfo URI of this module
+# HelpInfoURI = ''
+
+# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+# DefaultCommandPrefix = ''
+
+}
+

+ 111 - 0
Scripts/Terminal.Gui.PowerShell.Git.psm1

@@ -0,0 +1,111 @@
+<#
+  .SYNOPSIS
+  Creates a new branch with the specified name.
+  .DESCRIPTION
+  Creates a new branch with the specified name.
+  .PARAMETER Name
+  The name of the new branch.
+  Always required.
+  Must match the .net regex pattern "v2_\d{4}_[a-zA-Z0-9()_-]+".
+  Must also otherwise be a valid identifier for a git branch and follow any other project guidelines.
+  .PARAMETER NoSwitch
+  If specified, does not automatically switch to your new branch after creating it.
+  Default is to switch to the new branch after creating it.
+  .PARAMETER Push
+  If specified, automatically pushes the new branch to your remote after creating it.
+  .PARAMETER Remote
+  The name of the git remote, as configured.
+  If you never explicitly set this yourself, it is typically "origin".
+  If you only have one remote defined or have not explicitly set a remote yourself, do not provide this parameter; It will be detected automatically.
+  .INPUTS
+  None
+  .OUTPUTS
+  The name of the current branch after the operation, as a String.
+  If NoSwitch was specified and the operation succeeded, this should be the source branch.
+  If NoSwith was not specified or was explicitly set to $false and the operation succeeded, this should be the new branch.
+  If an exception occurs, does not return. Exceptions are unhandled and are the responsibility of the caller.
+  .NOTES
+  Errors thrown by git commands are not explicitly handled.
+#>
+Function New-GitBranch {
+  [CmdletBinding(PositionalBinding=$false, SupportsShouldProcess=$true, ConfirmImpact="Low", DefaultParameterSetName="Basic")]
+  param(
+    [Parameter(Mandatory=$true, ParameterSetName="Basic")]
+    [Parameter(Mandatory=$true, ParameterSetName="NoSwitch")]
+    [Parameter(Mandatory=$true, ParameterSetName="Push")]
+    [ValidatePattern("v2_\d{4}_[a-zA-Z0-9()_-]+")]
+    [string]$Name,
+    [Parameter(Mandatory=$true,ParameterSetName="NoSwitch",DontShow)]
+    [switch]$NoSwitch,
+    [Parameter(Mandatory=$false, ParameterSetName="Basic")]
+    [Parameter(Mandatory=$true, ParameterSetName="Push")]
+    [switch]$Push,
+    [Parameter(Mandatory=$false, ParameterSetName="Push")]
+    [string]$Remote = $null
+  )
+  $currentBranch = (& git branch --show-current)
+
+  if(!$PSCmdlet.ShouldProcess("Creating new branch named $Name from $currentBranch", $Name, "Creating branch")) {
+    return $null
+  }
+
+  git branch $Name
+
+  if(!$NoSwitch) {
+    git switch $Name
+
+    if($Push) {
+      if([String]::IsNullOrWhiteSpace($Remote)) {
+        $tempRemotes = (git remote show)
+        if($tempRemotes -is [array]){
+          # If we've gotten here, Push was specified, a remote was not specified or was blank, and there are multiple remotes defined locally.
+          # Not going to support that. Just error out.
+          Remove-Variable tempRemotes
+          throw "No Remote specified and multiple remotes are defined. Cannot continue."
+        } else {
+          # Push is set, Remote wasn't, but there's only one defined. Safe to continue. Use the only remote.
+          $Remote = $tempRemotes
+          Remove-Variable tempRemotes
+        }
+      }
+
+      # Push is set, and either Remote was specified or there's only one remote defined and we will use that.
+      # Perform the push. 
+      git push --set-upstream $Remote $Name
+    }
+  } else{
+    # NoSwitch was specified.
+    # Return the current branch name.
+    return $currentBranch
+  }
+
+  # If we made it to this point, return the Name that was specified.
+  return $Name
+}
+
+<#
+  .SYNOPSIS
+  Checks if the command 'git' is available in the current session.
+  .DESCRIPTION
+  Checks if the command 'git' is available in the current session.
+  Throws an error if not.
+  Returns $true if git is available.
+  Only intended for use in scripts and module manifests.
+  .INPUTS
+  None
+  .OUTPUTS
+  If git exists, $true.
+  Otherwise, $false.
+#>
+Function Test-GitAvailable {
+  [OutputType([Boolean])]
+  [CmdletBinding()]
+  param()
+  if($null -eq (Get-Command git -ErrorAction Ignore)) {
+    Write-Error -Message "git was not found. Git functionality will not work." -Category ObjectNotFound -TargetObject "git"
+    return $false
+  }
+  return $true
+}
+
+Test-GitAvailable -ErrorAction Continue

+ 150 - 0
Scripts/Terminal.Gui.PowerShell.psd1

@@ -0,0 +1,150 @@
+<#
+  .SYNOPSIS
+  All-inclusive module that includes all other Terminal.Gui.PowerShell.* modules.
+  .DESCRIPTION
+  All-inclusive module that includes all other Terminal.Gui.PowerShell.* modules.
+  .EXAMPLE
+  Import-Module ./Terminal.Gui.PowerShell.psd1
+  .NOTES
+  Doc comments on manifest files are not supported by Get-Help as of PowerShell 7.4.2.
+  This comment block is purely informational and will not interfere with module loading.
+#>
+
+
+@{
+
+# No root module because this is a manifest module.
+RootModule = ''
+
+# Version number of this module.
+ModuleVersion = '1.0.0'
+
+# Supported PSEditions
+CompatiblePSEditions = @('Core')
+
+# ID used to uniquely identify this module
+GUID = 'f28198f9-cf4b-4ab0-9f94-aef5616b7989'
+
+# Author of this module
+Author = 'Brandon Thetford (GitHub @dodexahedron)'
+
+# Company or vendor of this module
+CompanyName = 'The Terminal.Gui Project'
+
+# Copyright statement for this module
+Copyright = 'Brandon Thetford (GitHub @dodexahedron), provided to the Terminal.Gui project and you under the MIT license'
+
+# Description of the functionality provided by this module
+Description = 'Utilities for development-time operations on and management of components of Terminal.Gui code and other assets.'
+
+# Minimum version of the PowerShell engine required by this module
+PowerShellVersion = '7.4.0'
+
+# Name of the PowerShell "host" subsystem (not system host name). Helps ensure that we know what to expect from the environment.
+PowerShellHostName = 'ConsoleHost'
+
+# Minimum version of the PowerShell host required by this module
+PowerShellHostVersion = '7.4.0'
+
+# Processor architecture (None, MSIL, X86, IA64, Amd64, Arm, or an empty string) required by this module. One value only.
+# Set to AMD64 here because development on Terminal.Gui isn't really supported on anything else.
+# Has nothing to do with runtime use of Terminal.Gui.
+ProcessorArchitecture = 'Amd64'
+
+# Modules that must be imported into the global environment prior to importing this module
+RequiredModules = @(
+    @{
+        ModuleName='Microsoft.PowerShell.Utility'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='Microsoft.PowerShell.Management'
+        ModuleVersion='7.0.0'
+    },
+    @{
+        ModuleName='PSReadLine'
+        ModuleVersion='2.3.4'
+    }
+)
+
+# Assemblies that must be loaded prior to importing this module
+# RequiredAssemblies = @()
+
+# Script files (.ps1) that are run in the caller's environment prior to importing this module.
+# ScriptsToProcess = @()
+
+# Type files (.ps1xml) to be loaded when importing this module
+# TypesToProcess = @()
+
+# Format files (.ps1xml) to be loaded when importing this module
+# FormatsToProcess = @()
+
+# Modules to import as nested modules of this module.
+# This module is just a shortcut that loads all of our modules.
+NestedModules = @('./Terminal.Gui.PowerShell.Core.psd1', './Terminal.Gui.PowerShell.Analyzers.psd1', './Terminal.Gui.PowerShell.Git.psd1', './Terminal.Gui.PowerShell.Build.psd1')
+
+# Functions to export from this module.
+# Not filtered, so exports all functions exported by all nested modules.
+FunctionsToExport = '*'
+
+# Cmdlets to export from this module.
+# We don't have any, so empty array.
+CmdletsToExport = @()
+
+# Variables to export from this module.
+# We explicitly control scope of variables, so empty array.
+VariablesToExport = @()
+
+# Aliases to export from this module.
+# None defined at this time.
+AliasesToExport = @()
+
+# List of all modules packaged with this module
+# This is informational ONLY, so it's just blank right now.
+# ModuleList = @()
+
+# List of all files packaged with this module
+# This is informational ONLY, so it's just blank right now.
+# FileList = @()
+
+# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+PrivateData = @{
+
+    PSData = @{
+
+        # Tags applied to this module. These help with module discovery in online galleries.
+        # Tags = @()
+
+        # A URL to the license for this module.
+        LicenseUri = 'https://github.com/gui-cs/Terminal.Gui/tree/v2_develop/Scripts/COPYRIGHT'
+
+        # A URL to the main website for this project.
+        ProjectUri = 'https://github.com/gui-cs/Terminal.Gui'
+
+        # A URL to an icon representing this module.
+        # IconUri = ''
+
+        # ReleaseNotes of this module
+        ReleaseNotes = 'See change history and releases for Terminal.Gui on GitHub'
+
+        # Prerelease string of this module
+        # Prerelease = ''
+
+        # Flag to indicate whether the module requires explicit user acceptance for install/update/save
+        RequireLicenseAcceptance = $false
+
+        # External dependent modules of this module
+        # ExternalModuleDependencies = @()
+
+    } # End of PSData hashtable
+
+} # End of PrivateData hashtable
+
+# HelpInfo URI of this module
+# HelpInfoURI = ''
+
+# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+# DefaultCommandPrefix = ''
+
+}
+

+ 117 - 0
Scripts/Terminal.Gui.Powershell.Analyzers.psd1

@@ -0,0 +1,117 @@
+#
+# Module manifest for module 'Terminal.Gui.Powershell.Analyzers'
+#
+# Generated by: Brandon Thetford (GitHub @dodexahedron)
+#
+# Generated on: 4/24/2024
+#
+
+@{
+
+# Script module or binary module file associated with this manifest.
+RootModule = ''
+
+# Version number of this module.
+ModuleVersion = '1.0.0'
+
+# Supported PSEditions
+CompatiblePSEditions = @('Core')
+
+# ID used to uniquely identify this module
+GUID = '3e85001d-6539-4cf1-b71c-ec9e983f7fc8'
+
+# Author of this module
+Author = 'Brandon Thetford (GitHub @dodexahedron)'
+
+# Company or vendor of this module
+CompanyName = 'The Terminal.Gui Project'
+
+# Copyright statement for this module
+Copyright = '(c) Brandon Thetford (GitHub @dodexahedron). Provided to the Terminal.Gui project and you under the terms of the MIT License.'
+
+# Description of the functionality provided by this module
+Description = 'Operations involving Terminal.Gui analyzer projects, fur use during development of Terminal.Gui'
+
+# Minimum version of the PowerShell engine required by this module
+PowerShellVersion = '7.4.0'
+
+# Name of the PowerShell host required by this module
+PowerShellHostName = 'ConsoleHost'
+
+# Minimum version of the PowerShell host required by this module
+# PowerShellHostVersion = ''
+
+# Processor architecture (None, X86, Amd64) required by this module
+ProcessorArchitecture = 'Amd64'
+
+# Modules that must be imported into the global environment prior to importing this module
+RequiredModules = @('Microsoft.PowerShell.Management','Microsoft.PowerShell.Utility','./Terminal.Gui.PowerShell.Core.psd1')
+
+# Assemblies that must be loaded prior to importing this module
+# RequiredAssemblies = @()
+
+# Script files (.ps1) that are run in the caller's environment prior to importing this module.
+# ScriptsToProcess = @()
+
+# Type files (.ps1xml) to be loaded when importing this module
+# TypesToProcess = @()
+
+# Format files (.ps1xml) to be loaded when importing this module
+# FormatsToProcess = @()
+
+# Modules to import as nested modules.
+NestedModules = @('./Terminal.Gui.PowerShell.Analyzers.psm1')
+
+# Functions to export from this module.
+FunctionsToExport = @('Build-Analyzers')
+
+# Cmdlets to export from this module.
+CmdletsToExport = @()
+
+# Variables to export from this module
+VariablesToExport = @()
+
+# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
+AliasesToExport = @()
+
+# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+PrivateData = @{
+
+    PSData = @{
+
+        # Tags applied to this module. These help with module discovery in online galleries.
+        # Tags = @()
+
+        # A URL to the license for this module.
+        LicenseUri = 'https://github.com/gui-cs/Terminal.Gui/Scripts/COPYRIGHT'
+
+        # A URL to the main website for this project.
+        ProjectUri = 'https://github.com/gui-cs/Terminal.Gui'
+
+        # A URL to an icon representing this module.
+        # IconUri = ''
+
+        # ReleaseNotes of this module
+        # ReleaseNotes = ''
+
+        # Prerelease string of this module
+        # Prerelease = ''
+
+        # Flag to indicate whether the module requires explicit user acceptance for install/update/save
+        # RequireLicenseAcceptance = $false
+
+        # External dependent modules of this module
+        # ExternalModuleDependencies = @()
+
+    } # End of PSData hashtable
+
+} # End of PrivateData hashtable
+
+# HelpInfo URI of this module
+# HelpInfoURI = ''
+
+# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+# DefaultCommandPrefix = ''
+
+}
+