Change VisualStudioDescriptionFactory to interface

This commit is contained in:
Dustin Campbell 2023-08-29 11:27:11 -07:00
Родитель 4ec65f6fcc
Коммит 7c378c8b75
7 изменённых файлов: 132 добавлений и 133 удалений

Просмотреть файл

@ -1,115 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Core.Imaging;
using Microsoft.VisualStudio.Text.Adornments;
namespace Microsoft.VisualStudio.Editor.Razor.Completion;
[Shared]
[Export(typeof(VisualStudioDescriptionFactory))]
internal class DefaultVisualStudioDescriptionFactory : VisualStudioDescriptionFactory
{
// Internal for testing
internal static readonly ContainerElement SeparatorElement = new(
ContainerElementStyle.Wrapped,
new ClassifiedTextElement(
new ClassifiedTextRun(PredefinedClassificationNames.Comment, "------------")));
// Hardcoding the Guid here to avoid a reference to Microsoft.VisualStudio.ImageCatalog.dll
// that is not present in Visual Studio for Mac
private static readonly Guid s_imageCatalogGuid = new("{ae27a6b0-e345-4288-96df-5eaf394ee369}");
private static readonly ImageElement s_propertyGlyph = new(
new ImageId(s_imageCatalogGuid, 2429), // KnownImageIds.Type = 2429
"Razor Attribute Glyph");
private static readonly ClassifiedTextRun s_spaceLiteral = new(PredefinedClassificationNames.Literal, " ");
private static readonly ClassifiedTextRun s_dotLiteral = new(PredefinedClassificationNames.Literal, ".");
public override ContainerElement CreateClassifiedDescription(AggregateBoundAttributeDescription completionDescription)
{
if (completionDescription is null)
{
throw new ArgumentNullException(nameof(completionDescription));
}
var descriptionElements = new List<object>();
foreach (var descriptionInfo in completionDescription.DescriptionInfos)
{
if (descriptionElements.Count > 0)
{
descriptionElements.Add(SeparatorElement);
}
var returnTypeClassification = PredefinedClassificationNames.Type;
if (TypeNameStringResolver.TryGetSimpleName(descriptionInfo.ReturnTypeName, out var returnTypeName))
{
returnTypeClassification = PredefinedClassificationNames.Keyword;
}
else
{
returnTypeName = descriptionInfo.ReturnTypeName;
}
var tagHelperTypeName = descriptionInfo.TypeName;
var tagHelperTypeNamePrefix = string.Empty;
var tagHelperTypeNameProper = tagHelperTypeName;
var lastDot = tagHelperTypeName.LastIndexOf('.');
if (lastDot > 0)
{
var afterLastDot = lastDot + 1;
// We're pulling apart the type name so the prefix looks like:
//
// Microsoft.AspnetCore.Components.
tagHelperTypeNamePrefix = tagHelperTypeName[..afterLastDot];
// And the type name looks like BindBinds
tagHelperTypeNameProper = tagHelperTypeName[afterLastDot..];
}
descriptionElements.Add(
new ContainerElement(
ContainerElementStyle.Wrapped,
s_propertyGlyph,
new ClassifiedTextElement(
new ClassifiedTextRun(returnTypeClassification, returnTypeName),
s_spaceLiteral,
new ClassifiedTextRun(PredefinedClassificationNames.Literal, tagHelperTypeNamePrefix),
new ClassifiedTextRun(PredefinedClassificationNames.Type, tagHelperTypeNameProper),
s_dotLiteral,
new ClassifiedTextRun(PredefinedClassificationNames.Identifier, descriptionInfo.PropertyName))));
if (descriptionInfo.Documentation != null)
{
descriptionElements.Add(
new ContainerElement(
ContainerElementStyle.Wrapped,
new ClassifiedTextElement(
new ClassifiedTextRun(PredefinedClassificationNames.NaturalLanguage, descriptionInfo.Documentation))));
}
}
var descriptionContainer = new ContainerElement(ContainerElementStyle.Stacked, descriptionElements);
return descriptionContainer;
}
private static class PredefinedClassificationNames
{
public const string Keyword = "keyword";
public const string Literal = "literal";
public const string Type = "Type";
public const string Identifier = "identifier";
public const string Comment = "comment";
public const string NaturalLanguage = "natural language";
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Text.Adornments;
namespace Microsoft.VisualStudio.Editor.Razor.Completion;
internal interface IVisualStudioDescriptionFactory
{
ContainerElement CreateClassifiedDescription(AggregateBoundAttributeDescription description);
}

Просмотреть файл

@ -40,7 +40,7 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
private readonly VisualStudioRazorParser _parser;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly ICompletionBroker _completionBroker;
private readonly VisualStudioDescriptionFactory _descriptionFactory;
private readonly IVisualStudioDescriptionFactory _descriptionFactory;
private readonly JoinableTaskFactory _joinableTaskFactory;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
@ -49,7 +49,7 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
VisualStudioRazorParser parser,
IRazorCompletionFactsService completionFactsService,
ICompletionBroker completionBroker,
VisualStudioDescriptionFactory descriptionFactory,
IVisualStudioDescriptionFactory descriptionFactory,
JoinableTaskFactory joinableTaskFactory)
{
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));

Просмотреть файл

@ -25,7 +25,7 @@ internal class RazorDirectiveAttributeCompletionSourceProvider : IAsyncCompletio
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly ICompletionBroker _completionBroker;
private readonly VisualStudioDescriptionFactory _descriptionFactory;
private readonly IVisualStudioDescriptionFactory _descriptionFactory;
private readonly JoinableTaskContext _joinableTaskContext;
[ImportingConstructor]
@ -33,7 +33,7 @@ internal class RazorDirectiveAttributeCompletionSourceProvider : IAsyncCompletio
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
IRazorCompletionFactsService completionFactsService,
ICompletionBroker completionBroker,
VisualStudioDescriptionFactory descriptionFactory,
IVisualStudioDescriptionFactory descriptionFactory,
JoinableTaskContext joinableTaskContext)
{
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));

Просмотреть файл

@ -1,12 +1,114 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Core.Imaging;
using Microsoft.VisualStudio.Text.Adornments;
namespace Microsoft.VisualStudio.Editor.Razor.Completion;
internal abstract class VisualStudioDescriptionFactory
[Shared]
[Export(typeof(IVisualStudioDescriptionFactory))]
internal class VisualStudioDescriptionFactory : IVisualStudioDescriptionFactory
{
public abstract ContainerElement CreateClassifiedDescription(AggregateBoundAttributeDescription completionDescription);
// Internal for testing
internal static readonly ContainerElement SeparatorElement = new(
ContainerElementStyle.Wrapped,
new ClassifiedTextElement(
new ClassifiedTextRun(PredefinedClassificationNames.Comment, "------------")));
// Hardcoding the Guid here to avoid a reference to Microsoft.VisualStudio.ImageCatalog.dll
// that is not present in Visual Studio for Mac
private static readonly Guid s_imageCatalogGuid = new("{ae27a6b0-e345-4288-96df-5eaf394ee369}");
private static readonly ImageElement s_propertyGlyph = new(
new ImageId(s_imageCatalogGuid, 2429), // KnownImageIds.Type = 2429
"Razor Attribute Glyph");
private static readonly ClassifiedTextRun s_spaceLiteral = new(PredefinedClassificationNames.Literal, " ");
private static readonly ClassifiedTextRun s_dotLiteral = new(PredefinedClassificationNames.Literal, ".");
public ContainerElement CreateClassifiedDescription(AggregateBoundAttributeDescription description)
{
if (description is null)
{
throw new ArgumentNullException(nameof(description));
}
var descriptionElements = new List<object>();
foreach (var descriptionInfo in description.DescriptionInfos)
{
if (descriptionElements.Count > 0)
{
descriptionElements.Add(SeparatorElement);
}
var returnTypeClassification = PredefinedClassificationNames.Type;
if (TypeNameStringResolver.TryGetSimpleName(descriptionInfo.ReturnTypeName, out var returnTypeName))
{
returnTypeClassification = PredefinedClassificationNames.Keyword;
}
else
{
returnTypeName = descriptionInfo.ReturnTypeName;
}
var tagHelperTypeName = descriptionInfo.TypeName;
var tagHelperTypeNamePrefix = string.Empty;
var tagHelperTypeNameProper = tagHelperTypeName;
var lastDot = tagHelperTypeName.LastIndexOf('.');
if (lastDot > 0)
{
var afterLastDot = lastDot + 1;
// We're pulling apart the type name so the prefix looks like:
//
// Microsoft.AspnetCore.Components.
tagHelperTypeNamePrefix = tagHelperTypeName[..afterLastDot];
// And the type name looks like BindBinds
tagHelperTypeNameProper = tagHelperTypeName[afterLastDot..];
}
descriptionElements.Add(
new ContainerElement(
ContainerElementStyle.Wrapped,
s_propertyGlyph,
new ClassifiedTextElement(
new ClassifiedTextRun(returnTypeClassification, returnTypeName),
s_spaceLiteral,
new ClassifiedTextRun(PredefinedClassificationNames.Literal, tagHelperTypeNamePrefix),
new ClassifiedTextRun(PredefinedClassificationNames.Type, tagHelperTypeNameProper),
s_dotLiteral,
new ClassifiedTextRun(PredefinedClassificationNames.Identifier, descriptionInfo.PropertyName))));
if (descriptionInfo.Documentation is { } documentation)
{
descriptionElements.Add(
new ContainerElement(
ContainerElementStyle.Wrapped,
new ClassifiedTextElement(
new ClassifiedTextRun(PredefinedClassificationNames.NaturalLanguage, documentation))));
}
}
return new ContainerElement(ContainerElementStyle.Stacked, descriptionElements);
}
private static class PredefinedClassificationNames
{
public const string Keyword = "keyword";
public const string Literal = "literal";
public const string Type = "Type";
public const string Identifier = "identifier";
public const string Comment = "comment";
public const string NaturalLanguage = "natural language";
}
}

Просмотреть файл

@ -23,7 +23,7 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
public void CreateClassifiedDescription_SingleDescription_NoSeparator()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("TheReturnType", "TheTypeName", "ThePropertyName", "The documentation"),
@ -33,14 +33,14 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
var result = factory.CreateClassifiedDescription(description);
// Assert
Assert.DoesNotContain(DefaultVisualStudioDescriptionFactory.SeparatorElement, result.Elements);
Assert.DoesNotContain(VisualStudioDescriptionFactory.SeparatorElement, result.Elements);
}
[Fact]
public void CreateClassifiedDescription_MultipleDescription_Separator()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("TheReturnType", "TheTypeName", "ThePropertyName", "The documentation"),
@ -51,14 +51,14 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
var result = factory.CreateClassifiedDescription(description);
// Assert
Assert.Contains(DefaultVisualStudioDescriptionFactory.SeparatorElement, result.Elements);
Assert.Contains(VisualStudioDescriptionFactory.SeparatorElement, result.Elements);
}
[Fact]
public void CreateClassifiedDescription_RepresentsReturnType()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("TheReturnType", "TheTypeName", "ThePropertyName", "The documentation"),
@ -76,7 +76,7 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
public void CreateClassifiedDescription_RepresentsTypeName()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("TheReturnType", "TheTypeName", "ThePropertyName", "The documentation"),
@ -94,7 +94,7 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
public void CreateClassifiedDescription_RepresentsPropertyName()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("TheReturnType", "TheTypeName", "ThePropertyName", "The documentation"),
@ -112,7 +112,7 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
public void CreateClassifiedDescription_RepresentsDocumentation()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("TheReturnType", "TheTypeName", "ThePropertyName", "The documentation"),
@ -130,7 +130,7 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
public void CreateClassifiedDescription_CanSimplifyKeywordReturnTypes()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("System.String", "TheTypeName", "ThePropertyName", "The documentation"),
@ -149,7 +149,7 @@ public class DefaultVisualStudioDescriptionFactoryTest : TestBase
public void CreateClassifiedDescription_CanRepresentMultipleDescriptions()
{
// Arrange
var factory = new DefaultVisualStudioDescriptionFactory();
var factory = new VisualStudioDescriptionFactory();
var description = new AggregateBoundAttributeDescription(new[]
{
new BoundAttributeDescriptionInfo("System.String", "TheTypeName", "ThePropertyName", "The documentation"),

Просмотреть файл

@ -46,7 +46,7 @@ public class RazorDirectiveAttributeCompletionSourceTest : ProjectSnapshotManage
// Arrange
var expectedResult = new ContainerElement(ContainerElementStyle.Wrapped);
var description = new AggregateBoundAttributeDescription(Array.Empty<BoundAttributeDescriptionInfo>());
var descriptionFactory = Mock.Of<VisualStudioDescriptionFactory>(factory => factory.CreateClassifiedDescription(description) == expectedResult, MockBehavior.Strict);
var descriptionFactory = Mock.Of<IVisualStudioDescriptionFactory>(factory => factory.CreateClassifiedDescription(description) == expectedResult, MockBehavior.Strict);
var source = new RazorDirectiveAttributeCompletionSource(
Dispatcher,
Mock.Of<VisualStudioRazorParser>(MockBehavior.Strict),
@ -242,7 +242,7 @@ public class RazorDirectiveAttributeCompletionSourceTest : ProjectSnapshotManage
Mock.Of<VisualStudioRazorParser>(MockBehavior.Strict),
Mock.Of<IRazorCompletionFactsService>(MockBehavior.Strict),
Mock.Of<ICompletionBroker>(MockBehavior.Strict),
Mock.Of<VisualStudioDescriptionFactory>(MockBehavior.Strict),
Mock.Of<IVisualStudioDescriptionFactory>(MockBehavior.Strict),
JoinableTaskFactory);
return source;
}