Skip to content

Commit fd46d05

Browse files
committed
Created wrappers for Visual Studio's font and color classes.
1 parent 4230c40 commit fd46d05

21 files changed

+1735
-21
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using Microsoft.VisualStudio.Shell;
3+
4+
namespace Community.VisualStudio.Toolkit
5+
{
6+
/// <summary>
7+
/// Registers font and color definitions in Visual Studio.
8+
/// </summary>
9+
[AttributeUsage(AttributeTargets.Class)]
10+
public class ProvideFontsAndColorsAttribute : ProvideServiceAttributeBase
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="ProvideFontsAndColorsAttribute"/> class.
14+
/// The <paramref name="providerType"/> will also be provided as a service.
15+
/// </summary>
16+
/// <param name="providerType">The type of the <see cref="BaseFontAndColorProvider"/> implementation to provide.</param>
17+
public ProvideFontsAndColorsAttribute(Type providerType)
18+
: base(providerType, "Services")
19+
{
20+
ProviderType = providerType;
21+
}
22+
23+
/// <summary>
24+
/// The <see cref="BaseFontAndColorProvider"/> implementation to provide.
25+
/// </summary>
26+
public Type ProviderType { get; }
27+
28+
/// <inheritdoc/>
29+
public override void Register(RegistrationContext context)
30+
{
31+
if (context is not null)
32+
{
33+
foreach (Type categoryType in BaseFontAndColorProvider.GetCategoryTypes(ProviderType))
34+
{
35+
using (Key key = context.CreateKey($"FontAndColors\\{categoryType.FullName}"))
36+
{
37+
key.SetValue("Category", categoryType.GUID.ToString("B"));
38+
key.SetValue("Package", ProviderType.GUID.ToString("B"));
39+
}
40+
}
41+
}
42+
43+
base.Register(context);
44+
}
45+
46+
/// <inheritdoc/>
47+
public override void Unregister(RegistrationContext context)
48+
{
49+
if (context is not null)
50+
{
51+
foreach (Type categoryType in BaseFontAndColorProvider.GetCategoryTypes(ProviderType))
52+
{
53+
context.RemoveKey($"FontAndColors\\{categoryType.FullName}");
54+
}
55+
}
56+
57+
base.Unregister(context);
58+
}
59+
}
60+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace System.Diagnostics.CodeAnalysis
2+
{
3+
/// <summary>
4+
/// This is a copy of the attribute of the same name from .NET 5+ to allow it to be used in .NET Framework.
5+
/// https://github.com/dotnet/runtime/blob/47071da67320985a10f4b70f50f894ab411f4994/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs#L137
6+
/// </summary>
7+
internal class MemberNotNullAttribute : Attribute
8+
{
9+
public MemberNotNullAttribute(string member) => Members = new[] { member };
10+
public MemberNotNullAttribute(params string[] members) => Members = members;
11+
public string[] Members { get; }
12+
}
13+
}

src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,15 @@ public static class AsyncPackageExtensions
2121
/// <returns>A collection of the command instances</returns>
2222
public static async Task<IEnumerable<object>> RegisterCommandsAsync(this AsyncPackage package, params Assembly[] assemblies)
2323
{
24-
List<Assembly> assembliesList = assemblies.ToList();
25-
Assembly packageAssembly = package.GetType().Assembly;
26-
if (!assembliesList.Contains(packageAssembly))
27-
assembliesList.Add(packageAssembly);
28-
2924
Type baseCommandType = typeof(BaseCommand<>);
30-
IEnumerable<Type> commandTypes = assembliesList.SelectMany(x => x.GetTypes())
25+
IEnumerable<Type> commandTypes = IncludePackageAssembly(assemblies, package).SelectMany(x => x.GetTypes())
3126
.Where(x =>
3227
!x.IsAbstract
3328
&& x.IsAssignableToGenericType(baseCommandType)
3429
&& x.GetCustomAttribute<CommandAttribute>() != null);
3530

3631
List<object> commands = new();
37-
foreach (Type? commandType in commandTypes)
32+
foreach (Type commandType in commandTypes)
3833
{
3934
MethodInfo initializeAsyncMethod = commandType.GetMethod(
4035
nameof(BaseCommand<object>.InitializeAsync),
@@ -56,18 +51,13 @@ public static async Task<IEnumerable<object>> RegisterCommandsAsync(this AsyncPa
5651
/// <returns></returns>
5752
public static void RegisterToolWindows(this AsyncPackage package, params Assembly[] assemblies)
5853
{
59-
List<Assembly> assembliesList = assemblies.ToList();
60-
Assembly packageAssembly = package.GetType().Assembly;
61-
if (!assembliesList.Contains(packageAssembly))
62-
assembliesList.Add(packageAssembly);
63-
6454
Type baseToolWindowType = typeof(BaseToolWindow<>);
65-
IEnumerable<Type> toolWindowTypes = assembliesList.SelectMany(x => x.GetTypes())
55+
IEnumerable<Type> toolWindowTypes = IncludePackageAssembly(assemblies, package).SelectMany(x => x.GetTypes())
6656
.Where(x =>
6757
!x.IsAbstract
6858
&& x.IsAssignableToGenericType(baseToolWindowType));
6959

70-
foreach (Type? toolWindowtype in toolWindowTypes)
60+
foreach (Type toolWindowtype in toolWindowTypes)
7161
{
7262
MethodInfo initializeMethod = toolWindowtype.GetMethod(
7363
"Initialize",
@@ -76,5 +66,38 @@ public static void RegisterToolWindows(this AsyncPackage package, params Assembl
7666
initializeMethod.Invoke(null, new object[] { package });
7767
}
7868
}
69+
70+
/// <summary>
71+
/// Finds, creates and registers <see cref="BaseFontAndColorProvider"/> implementations.
72+
/// </summary>
73+
/// <param name="package">The package to register the provider in.</param>
74+
/// <param name="assemblies">Additional assemblies to scan. The assembly that <paramref name="package"/> is in will always be scanned.</param>
75+
/// <returns>A task.</returns>
76+
public static async Task RegisterFontAndColorProvidersAsync(this AsyncPackage package, params Assembly[] assemblies)
77+
{
78+
Type baseProviderType = typeof(BaseFontAndColorProvider);
79+
IEnumerable<Type> providerTypes = IncludePackageAssembly(assemblies, package)
80+
.SelectMany(x => x.GetTypes())
81+
.Where(x => !x.IsAbstract && baseProviderType.IsAssignableFrom(x));
82+
83+
foreach (Type providerType in providerTypes)
84+
{
85+
ConstructorInfo? ctor = providerType.GetConstructor(Type.EmptyTypes)
86+
?? throw new InvalidOperationException($"The type '{providerType.Name}' must have a parameterless constructor.");
87+
BaseFontAndColorProvider provider = (BaseFontAndColorProvider)ctor.Invoke(Array.Empty<object>());
88+
await provider.InitializeAsync(package);
89+
}
90+
}
91+
92+
private static IReadOnlyList<Assembly> IncludePackageAssembly(IEnumerable<Assembly> assemblies, AsyncPackage package)
93+
{
94+
List<Assembly> list = assemblies.ToList();
95+
Assembly packageAssembly = package.GetType().Assembly;
96+
if (!list.Contains(packageAssembly))
97+
{
98+
list.Add(packageAssembly);
99+
}
100+
return list;
101+
}
79102
}
80103
}

0 commit comments

Comments
 (0)