mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-31 02:08:03 +01:00
Merge branch 'v2_develop' into v2_textview-textfield-cursor-fix_3431
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
"""));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
## Release 1.0
|
||||
|
||||
### New Rules
|
||||
|
||||
Rule ID | Category | Severity | Notes
|
||||
--------|----------|----------|--------------------
|
||||
TG0001 | Usage | Error | TG0001_GlobalNamespaceNotSupported
|
||||
TG0002 | Usage | Error | TG0002_UnderlyingTypeNotSupported
|
||||
@@ -0,0 +1,4 @@
|
||||
### New Rules
|
||||
|
||||
Rule ID | Category | Severity | Notes
|
||||
--------|----------|----------|--------------------
|
||||
@@ -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)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
N:System.Runtime.CompilerServices
|
||||
N:System.Diagnostics.CodeAnalysis
|
||||
N:System.Numerics
|
||||
@@ -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}";
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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"/> && !<see cref="FastHasFlags"/> && <see cref="ClassName"/>
|
||||
/// <see langword="is"/> <see langword="null"/> && <see cref="ClassNamespace"/> <see langword="is"/>
|
||||
/// <see langword="null"/>
|
||||
/// </remarks>
|
||||
public override bool IsDefaultAttribute ()
|
||||
{
|
||||
return FastIsDefined
|
||||
&& !FastHasFlags
|
||||
&& ClassName is null
|
||||
&& ClassNamespace is null;
|
||||
}
|
||||
}
|
||||
@@ -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 <<
|
||||
/// <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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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>;
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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)); }
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs
Normal file
202
Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"/> && !<see cref="FastHasFlags"/> && <see cref="ClassName"/>
|
||||
/// <see langword="is"/> <see langword="null"/> && <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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 <<
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"InternalAnalyzers Debug": {
|
||||
"commandName": "DebugRoslynComponent",
|
||||
"targetProject": "..\\Terminal.Gui.Analyzers.Internal.Debugging\\Terminal.Gui.Analyzers.Internal.Debugging.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>
|
||||
@@ -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
Directory.build.props
Normal file
6
Directory.build.props
Normal file
@@ -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
Scripts/COPYRIGHT
Normal file
16
Scripts/COPYRIGHT
Normal file
@@ -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
Scripts/README.md
Normal file
46
Scripts/README.md
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Analyzers.psm1
Normal file
105
Scripts/Terminal.Gui.PowerShell.Analyzers.psm1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Build.psd1
Normal file
131
Scripts/Terminal.Gui.PowerShell.Build.psd1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Build.psm1
Normal file
32
Scripts/Terminal.Gui.PowerShell.Build.psm1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Core.psd1
Normal file
138
Scripts/Terminal.Gui.PowerShell.Core.psd1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Core.psm1
Normal file
143
Scripts/Terminal.Gui.PowerShell.Core.psm1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Git.psd1
Normal file
135
Scripts/Terminal.Gui.PowerShell.Git.psd1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.Git.psm1
Normal file
111
Scripts/Terminal.Gui.PowerShell.Git.psm1
Normal file
@@ -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
Scripts/Terminal.Gui.PowerShell.psd1
Normal file
150
Scripts/Terminal.Gui.PowerShell.psd1
Normal file
@@ -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
Scripts/Terminal.Gui.Powershell.Analyzers.psd1
Normal file
117
Scripts/Terminal.Gui.Powershell.Analyzers.psd1
Normal file
@@ -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 = ''
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user