Merge pull request #9194 from DustinCampbell/completion-cleanup

Clean up completion to return ImmutableArray<T> rather than IReadOnlyList<T>
This commit is contained in:
Dustin Campbell 2023-08-30 10:05:57 -07:00 коммит произвёл GitHub
Родитель f3fac0332f 7c378c8b75
Коммит 548eefdfed
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
36 изменённых файлов: 499 добавлений и 437 удалений

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

@ -2,7 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
@ -39,9 +39,9 @@ internal class DirectiveAttributeTransitionCompletionItemProvider : DirectiveAtt
}
}
private static readonly IReadOnlyList<RazorCompletionItem> s_completions = new[] { TransitionCompletionItem };
private static readonly ImmutableArray<RazorCompletionItem> s_completions = ImmutableArray.Create(TransitionCompletionItem);
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
public override ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
@ -51,13 +51,13 @@ internal class DirectiveAttributeTransitionCompletionItemProvider : DirectiveAtt
if (!FileKinds.IsComponent(context.SyntaxTree.Options.FileKind))
{
// Directive attributes are only supported in components
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var owner = context.Owner;
if (owner is null)
{
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var attribute = owner.Parent;
@ -69,19 +69,19 @@ internal class DirectiveAttributeTransitionCompletionItemProvider : DirectiveAtt
if (!TryGetAttributeInfo(owner, out var prefixLocation, out var attributeName, out var attributeNameLocation, out _, out _))
{
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (attributeNameLocation.IntersectsWith(context.AbsoluteIndex) && attributeName.StartsWith("@", StringComparison.Ordinal))
{
// The transition is already provided for the attribute name
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!IsValidCompletionPoint(context.AbsoluteIndex, prefixLocation, attributeNameLocation))
{
// Not operating in the attribute name area
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
// This represents a tag when there's no attribute content <InputText | />.

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

@ -2,7 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -23,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
// for this legacy version.
internal class LegacyRazorCompletionEndpoint : IVSCompletionEndpoint
{
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly CompletionListCache _completionListCache;
private static readonly Command s_retriggerCompletionCommand = new()
{
@ -34,22 +35,10 @@ internal class LegacyRazorCompletionEndpoint : IVSCompletionEndpoint
public bool MutatesSolutionState => false;
public LegacyRazorCompletionEndpoint(
RazorCompletionFactsService completionFactsService,
CompletionListCache completionListCache)
public LegacyRazorCompletionEndpoint(IRazorCompletionFactsService completionFactsService, CompletionListCache completionListCache)
{
if (completionFactsService is null)
{
throw new ArgumentNullException(nameof(completionFactsService));
}
if (completionListCache is null)
{
throw new ArgumentNullException(nameof(completionListCache));
}
_completionFactsService = completionFactsService;
_completionListCache = completionListCache;
_completionFactsService = completionFactsService ?? throw new ArgumentNullException(nameof(completionFactsService));
_completionListCache = completionListCache ?? throw new ArgumentNullException(nameof(completionListCache));
}
public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
@ -107,7 +96,7 @@ internal class LegacyRazorCompletionEndpoint : IVSCompletionEndpoint
var razorCompletionItems = _completionFactsService.GetCompletionItems(completionContext);
requestContext.Logger.LogTrace("Resolved {razorCompletionItemsCount} completion items.", razorCompletionItems.Count);
requestContext.Logger.LogTrace("Resolved {razorCompletionItemsCount} completion items.", razorCompletionItems.Length);
var completionList = CreateLSPCompletionList(razorCompletionItems);
var completionCapability = _clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
@ -140,31 +129,33 @@ internal class LegacyRazorCompletionEndpoint : IVSCompletionEndpoint
}
// Internal for testing
internal VSInternalCompletionList CreateLSPCompletionList(IReadOnlyList<RazorCompletionItem> razorCompletionItems) => CreateLSPCompletionList(razorCompletionItems, _clientCapabilities!);
internal VSInternalCompletionList CreateLSPCompletionList(ImmutableArray<RazorCompletionItem> razorCompletionItems)
=> CreateLSPCompletionList(razorCompletionItems, _clientCapabilities!);
// Internal for benchmarking and testing
internal static VSInternalCompletionList CreateLSPCompletionList(
IReadOnlyList<RazorCompletionItem> razorCompletionItems,
ImmutableArray<RazorCompletionItem> razorCompletionItems,
VSInternalClientCapabilities clientCapabilities)
{
var completionItems = new List<CompletionItem>();
using var items = new PooledArrayBuilder<CompletionItem>();
foreach (var razorCompletionItem in razorCompletionItems)
{
if (TryConvert(razorCompletionItem, clientCapabilities, out var completionItem))
{
completionItems.Add(completionItem);
items.Add(completionItem);
}
}
var completionList = new VSInternalCompletionList()
{
Items = completionItems.ToArray(),
Items = items.ToArray(),
IsIncomplete = false,
};
var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
var optimizedCompletionList = CompletionListOptimizer.Optimize(completionList, completionCapability);
return optimizedCompletionList;
return CompletionListOptimizer.Optimize(completionList, completionCapability);
}
// Internal for testing

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

@ -12,6 +12,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -20,7 +21,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
internal class RazorCompletionListProvider
{
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly CompletionListCache _completionListCache;
private readonly ILogger<RazorCompletionListProvider> _logger;
private static readonly Command s_retriggerCompletionCommand = new()
@ -30,7 +31,7 @@ internal class RazorCompletionListProvider
};
public RazorCompletionListProvider(
RazorCompletionFactsService completionFactsService,
IRazorCompletionFactsService completionFactsService,
CompletionListCache completionListCache,
ILoggerFactory loggerFactory)
{
@ -63,6 +64,7 @@ internal class RazorCompletionListProvider
CompletionTriggerKind.TriggerCharacter => CompletionReason.Typing,
_ => CompletionReason.Typing,
};
var completionOptions = new RazorCompletionOptions(SnippetsSupported: true);
var syntaxTree = await documentContext.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var tagHelperContext = await documentContext.GetTagHelperContextAsync(cancellationToken).ConfigureAwait(false);
@ -79,7 +81,7 @@ internal class RazorCompletionListProvider
var razorCompletionItems = _completionFactsService.GetCompletionItems(razorCompletionContext);
_logger.LogTrace("Resolved {razorCompletionItemsCount} completion items.", razorCompletionItems.Count);
_logger.LogTrace("Resolved {razorCompletionItemsCount} completion items.", razorCompletionItems.Length);
var completionList = CreateLSPCompletionList(razorCompletionItems, clientCapabilities);
@ -94,27 +96,28 @@ internal class RazorCompletionListProvider
// Internal for benchmarking and testing
internal static VSInternalCompletionList CreateLSPCompletionList(
IReadOnlyList<RazorCompletionItem> razorCompletionItems,
ImmutableArray<RazorCompletionItem> razorCompletionItems,
VSInternalClientCapabilities clientCapabilities)
{
var completionItems = new List<CompletionItem>();
using var items = new PooledArrayBuilder<CompletionItem>();
foreach (var razorCompletionItem in razorCompletionItems)
{
if (TryConvert(razorCompletionItem, clientCapabilities, out var completionItem))
{
completionItems.Add(completionItem);
items.Add(completionItem);
}
}
var completionList = new VSInternalCompletionList()
{
Items = completionItems.ToArray(),
Items = items.ToArray(),
IsIncomplete = false,
};
var completionCapability = clientCapabilities.TextDocument?.Completion as VSInternalCompletionSetting;
var optimizedCompletionList = CompletionListOptimizer.Optimize(completionList, completionCapability);
return optimizedCompletionList;
return CompletionListOptimizer.Optimize(completionList, completionCapability);
}
// Internal for testing

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

@ -3,11 +3,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.Extensions.Options;
@ -15,7 +17,7 @@ using Microsoft.VisualStudio.Editor.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
internal class TagHelperCompletionProvider : RazorCompletionItemProvider
internal class TagHelperCompletionProvider : IRazorCompletionItemProvider
{
// Internal for testing
internal static readonly IReadOnlyList<RazorCommitCharacter> MinimizedAttributeCommitCharacters = RazorCommitCharacter.FromArray(new[] { "=", " " });
@ -41,7 +43,7 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
_optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
}
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
public ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
@ -52,7 +54,7 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
if (owner is null)
{
Debug.Fail("Owner should never be null.");
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var parent = owner.Parent;
@ -102,13 +104,13 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
//
// Will be interpreted as having an `@code` attribute name due to multi-line attributes being a thing. Ultimately this is mostly a
// heuristic that we have to apply in order to workaround limitations of the Razor compiler.
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
var attributeCompletions = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, context.TagHelperDocumentContext, context.Options);
return attributeCompletions;
return GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, context.TagHelperDocumentContext, context.Options);
static bool InOrAtEndOfAttribute(SyntaxNode attributeSyntax, int absoluteIndex)
{
// When we are in the middle of writing an attribute it is treated as a minimilized one, e.g.:
@ -123,10 +125,10 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
}
// Invalid location for TagHelper completions.
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
private IReadOnlyList<RazorCompletionItem> GetAttributeCompletions(
private ImmutableArray<RazorCompletionItem> GetAttributeCompletions(
SyntaxNode containingAttribute,
string containingTagName,
string? selectedAttributeName,
@ -148,8 +150,9 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
ancestorIsTagHelper,
HtmlFactsService.IsHtmlTagName);
var completionItems = new List<RazorCompletionItem>();
using var completionItems = new PooledArrayBuilder<RazorCompletionItem>();
var completionResult = _tagHelperCompletionService.GetAttributeCompletions(attributeCompletionContext);
foreach (var completion in completionResult.Completions)
{
var filterText = completion.Key;
@ -202,13 +205,14 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
return descriptionInfo;
});
var attributeDescriptionInfo = new AggregateBoundAttributeDescription(attributeDescriptions.ToList());
razorCompletionItem.SetAttributeCompletionDescription(attributeDescriptionInfo);
completionItems.Add(razorCompletionItem);
}
return completionItems;
return completionItems.DrainToImmutable();
}
private bool TryResolveInsertText(string baseInsertText, AttributeContext context, [NotNullWhen(true)] out string? snippetText)
@ -231,7 +235,7 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
return false;
}
private IReadOnlyList<RazorCompletionItem> GetElementCompletions(
private ImmutableArray<RazorCompletionItem> GetElementCompletions(
SyntaxNode containingElement,
string containingTagName,
IEnumerable<KeyValuePair<string, string>> attributes,
@ -248,7 +252,7 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
ancestorIsTagHelper,
HtmlFactsService.IsHtmlTagName);
var completionItems = new List<RazorCompletionItem>();
using var completionItems = new PooledArrayBuilder<RazorCompletionItem>();
var completionResult = _tagHelperCompletionService.GetElementCompletions(elementCompletionContext);
foreach (var completion in completionResult.Completions)
{
@ -265,7 +269,7 @@ internal class TagHelperCompletionProvider : RazorCompletionItemProvider
completionItems.Add(razorCompletionItem);
}
return completionItems;
return completionItems.DrainToImmutable();
}
private const string BooleanTypeString = "System.Boolean";

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

@ -93,13 +93,13 @@ internal static class IServiceCollectionExtensions
services.AddSingleton<CompletionItemResolver, RazorCompletionItemResolver>();
services.AddSingleton<CompletionItemResolver, DelegatedCompletionItemResolver>();
services.AddSingleton<TagHelperCompletionService, LanguageServerTagHelperCompletionService>();
services.AddSingleton<RazorCompletionFactsService, DefaultRazorCompletionFactsService>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveAttributeCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveAttributeParameterCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, DirectiveAttributeTransitionCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, MarkupTransitionCompletionItemProvider>();
services.AddSingleton<RazorCompletionItemProvider, TagHelperCompletionProvider>();
services.AddSingleton<IRazorCompletionFactsService, RazorCompletionFactsService>();
services.AddSingleton<IRazorCompletionItemProvider, DirectiveCompletionItemProvider>();
services.AddSingleton<IRazorCompletionItemProvider, DirectiveAttributeCompletionItemProvider>();
services.AddSingleton<IRazorCompletionItemProvider, DirectiveAttributeParameterCompletionItemProvider>();
services.AddSingleton<IRazorCompletionItemProvider, DirectiveAttributeTransitionCompletionItemProvider>();
services.AddSingleton<IRazorCompletionItemProvider, MarkupTransitionCompletionItemProvider>();
services.AddSingleton<IRazorCompletionItemProvider, TagHelperCompletionProvider>();
}
public static void AddDiagnosticServices(this IServiceCollection services)

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

@ -1,50 +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 System.Linq;
namespace Microsoft.CodeAnalysis.Razor.Completion;
[Shared]
[Export(typeof(RazorCompletionFactsService))]
internal class DefaultRazorCompletionFactsService : RazorCompletionFactsService
{
private readonly IReadOnlyList<RazorCompletionItemProvider> _completionItemProviders;
[ImportingConstructor]
public DefaultRazorCompletionFactsService([ImportMany] IEnumerable<RazorCompletionItemProvider> completionItemProviders)
{
if (completionItemProviders is null)
{
throw new ArgumentNullException(nameof(completionItemProviders));
}
_completionItemProviders = completionItemProviders.ToArray();
}
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.TagHelperDocumentContext is null)
{
throw new ArgumentNullException(nameof(context.TagHelperDocumentContext));
}
var completions = new List<RazorCompletionItem>();
for (var i = 0; i < _completionItemProviders.Count; i++)
{
var completionItemProvider = _completionItemProviders[i];
var items = completionItemProvider.GetCompletionItems(context);
completions.AddRange(items);
}
return completions;
}
}

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

@ -5,20 +5,20 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Editor.Razor;
namespace Microsoft.CodeAnalysis.Razor.Completion;
[Shared]
[Export(typeof(RazorCompletionItemProvider))]
[Export(typeof(IRazorCompletionItemProvider))]
internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeCompletionItemProviderBase
{
private static readonly RazorCompletionItem[] s_noDirectiveAttributeCompletionItems = Array.Empty<RazorCompletionItem>();
private readonly TagHelperFactsService _tagHelperFactsService;
[ImportingConstructor]
@ -32,7 +32,7 @@ internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeComp
_tagHelperFactsService = tagHelperFactsService;
}
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
public override ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
@ -47,31 +47,31 @@ internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeComp
if (!FileKinds.IsComponent(context.SyntaxTree.Options.FileKind))
{
// Directive attributes are only supported in components
return s_noDirectiveAttributeCompletionItems;
return ImmutableArray<RazorCompletionItem>.Empty;
}
var owner = context.Owner;
if (owner is null)
{
return s_noDirectiveAttributeCompletionItems;
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!TryGetAttributeInfo(owner, out _, out var attributeName, out var attributeNameLocation, out _, out _))
{
// Either we're not in an attribute or the attribute is so malformed that we can't provide proper completions.
return s_noDirectiveAttributeCompletionItems;
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!attributeNameLocation.IntersectsWith(context.AbsoluteIndex))
{
// We're trying to retrieve completions on a portion of the name that is not supported (such as a parameter).
return s_noDirectiveAttributeCompletionItems;
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!TryGetElementInfo(owner.Parent.Parent, out var containingTagName, out var attributes))
{
// This should never be the case, it means that we're operating on an attribute that doesn't have a tag.
return s_noDirectiveAttributeCompletionItems;
return ImmutableArray<RazorCompletionItem>.Empty;
}
// At this point we've determined that completions have been requested for the name portion of the selected attribute.
@ -86,11 +86,11 @@ internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeComp
return completionItems;
}
return s_noDirectiveAttributeCompletionItems;
return ImmutableArray<RazorCompletionItem>.Empty;
}
// Internal for testing
internal IReadOnlyList<RazorCompletionItem> GetAttributeCompletions(
internal ImmutableArray<RazorCompletionItem> GetAttributeCompletions(
string selectedAttributeName,
string containingTagName,
IEnumerable<string> attributes,
@ -100,7 +100,7 @@ internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeComp
if (descriptorsForTag.Count == 0)
{
// If the current tag has no possible descriptors then we can't have any directive attributes.
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
// Attributes are case sensitive when matching
@ -141,7 +141,8 @@ internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeComp
}
}
var completionItems = new List<RazorCompletionItem>();
using var completionItems = new PooledArrayBuilder<RazorCompletionItem>();
foreach (var completion in attributeCompletions)
{
var insertText = completion.Key;
@ -173,7 +174,7 @@ internal class DirectiveAttributeCompletionItemProvider : DirectiveAttributeComp
completionItems.Add(razorCompletionItem);
}
return completionItems;
return completionItems.DrainToImmutable();
bool TryAddCompletion(string attributeName, BoundAttributeDescriptor boundAttributeDescriptor, TagHelperDescriptor tagHelperDescriptor)
{

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

@ -5,14 +5,17 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using RazorSyntaxList = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxList<Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode>;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
namespace Microsoft.CodeAnalysis.Razor.Completion;
internal abstract class DirectiveAttributeCompletionItemProviderBase : RazorCompletionItemProvider
internal abstract class DirectiveAttributeCompletionItemProviderBase : IRazorCompletionItemProvider
{
public abstract ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context);
// Internal for testing
internal static bool TryGetAttributeInfo(
RazorSyntaxNode attributeLeafOwner,

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

@ -5,16 +5,18 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Editor.Razor;
namespace Microsoft.CodeAnalysis.Razor.Completion;
[Shared]
[Export(typeof(RazorCompletionItemProvider))]
[Export(typeof(IRazorCompletionItemProvider))]
internal class DirectiveAttributeParameterCompletionItemProvider : DirectiveAttributeCompletionItemProviderBase
{
private readonly TagHelperFactsService _tagHelperFactsService;
@ -30,7 +32,7 @@ internal class DirectiveAttributeParameterCompletionItemProvider : DirectiveAttr
_tagHelperFactsService = tagHelperFactsService;
}
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
public override ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
@ -45,39 +47,38 @@ internal class DirectiveAttributeParameterCompletionItemProvider : DirectiveAttr
if (!FileKinds.IsComponent(context.SyntaxTree.Options.FileKind))
{
// Directive attribute parameters are only supported in components
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var owner = context.Owner;
if (owner is null)
{
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!TryGetAttributeInfo(owner, out _, out var attributeName, out _, out var parameterName, out var parameterNameLocation))
{
// Either we're not in an attribute or the attribute is so malformed that we can't provide proper completions.
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!parameterNameLocation.IntersectsWith(context.AbsoluteIndex))
{
// We're trying to retrieve completions on a portion of the name that is not supported (such as the name, i.e., |@bind|:format).
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!TryGetElementInfo(owner.Parent.Parent, out var containingTagName, out var attributes))
{
// This should never be the case, it means that we're operating on an attribute that doesn't have a tag.
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var completions = GetAttributeParameterCompletions(attributeName, parameterName, containingTagName, attributes, context.TagHelperDocumentContext);
return completions;
return GetAttributeParameterCompletions(attributeName, parameterName, containingTagName, attributes, context.TagHelperDocumentContext);
}
// Internal for testing
internal IReadOnlyList<RazorCompletionItem> GetAttributeParameterCompletions(
internal ImmutableArray<RazorCompletionItem> GetAttributeParameterCompletions(
string attributeName,
string parameterName,
string containingTagName,
@ -88,7 +89,7 @@ internal class DirectiveAttributeParameterCompletionItemProvider : DirectiveAttr
if (descriptorsForTag.Count == 0)
{
// If the current tag has no possible descriptors then we can't have any additional attributes.
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
// Attribute parameters are case sensitive when matching
@ -130,7 +131,8 @@ internal class DirectiveAttributeParameterCompletionItemProvider : DirectiveAttr
}
}
var completionItems = new List<RazorCompletionItem>();
using var completionItems = new PooledArrayBuilder<RazorCompletionItem>();
foreach (var completion in attributeCompletions)
{
if (string.Equals(completion.Key, parameterName, StringComparison.Ordinal))
@ -150,6 +152,6 @@ internal class DirectiveAttributeParameterCompletionItemProvider : DirectiveAttr
completionItems.Add(razorCompletionItem);
}
return completionItems;
return completionItems.DrainToImmutable();
}
}

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

@ -5,17 +5,19 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.CodeAnalysis.Razor.Completion;
[Shared]
[Export(typeof(RazorCompletionItemProvider))]
internal class DirectiveCompletionItemProvider : RazorCompletionItemProvider
[Export(typeof(IRazorCompletionItemProvider))]
internal class DirectiveCompletionItemProvider : IRazorCompletionItemProvider
{
internal static readonly IReadOnlyList<RazorCommitCharacter> SingleLineDirectiveCommitCharacters = RazorCommitCharacter.FromArray(new[] { " " });
internal static readonly IReadOnlyList<RazorCommitCharacter> BlockDirectiveCommitCharacters = RazorCommitCharacter.FromArray(new[] { " ", "{" });
@ -46,21 +48,22 @@ internal class DirectiveCompletionItemProvider : RazorCompletionItemProvider
["typeparam"] = ("typeparam ${1:T}$0", "typeparam T")
};
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
public ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var completions = new List<RazorCompletionItem>();
using var completions = new PooledArrayBuilder<RazorCompletionItem>();
if (ShouldProvideCompletions(context))
{
var directiveCompletions = GetDirectiveCompletionItems(context.SyntaxTree);
completions.AddRange(directiveCompletions);
}
return completions;
return completions.DrainToImmutable();
}
// Internal for testing
@ -126,11 +129,13 @@ internal class DirectiveCompletionItemProvider : RazorCompletionItemProvider
}
// Internal for testing
internal static List<RazorCompletionItem> GetDirectiveCompletionItems(RazorSyntaxTree syntaxTree)
internal static ImmutableArray<RazorCompletionItem> GetDirectiveCompletionItems(RazorSyntaxTree syntaxTree)
{
var defaultDirectives = FileKinds.IsComponent(syntaxTree.Options.FileKind) ? Array.Empty<DirectiveDescriptor>() : s_defaultDirectives;
var directives = syntaxTree.Options.Directives.Concat(defaultDirectives);
var completionItems = new List<RazorCompletionItem>();
using var completionItems = new PooledArrayBuilder<RazorCompletionItem>();
foreach (var directive in directives)
{
var completionDisplayText = directive.DisplayName ?? directive.Directive;
@ -164,7 +169,7 @@ internal class DirectiveCompletionItemProvider : RazorCompletionItemProvider
}
}
return completionItems;
return completionItems.DrainToImmutable();
}
private static IReadOnlyList<RazorCommitCharacter> GetDirectiveCommitCharacters(DirectiveKind directiveKind)

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

@ -1,11 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Razor.Completion;
internal abstract class RazorCompletionItemProvider
internal interface IRazorCompletionFactsService
{
public abstract IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context);
ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext razorCompletionContext);
}

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

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Razor.Completion;
internal interface IRazorCompletionItemProvider
{
ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context);
}

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

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
@ -11,7 +12,7 @@ using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
namespace Microsoft.CodeAnalysis.Razor.Completion;
internal class MarkupTransitionCompletionItemProvider : RazorCompletionItemProvider
internal class MarkupTransitionCompletionItemProvider : IRazorCompletionItemProvider
{
private static readonly IReadOnlyList<RazorCommitCharacter> s_elementCommitCharacters = RazorCommitCharacter.FromArray(new[] { ">" });
@ -40,15 +41,10 @@ internal class MarkupTransitionCompletionItemProvider : RazorCompletionItemProvi
public MarkupTransitionCompletionItemProvider(HtmlFactsService htmlFactsService)
{
if (htmlFactsService is null)
{
throw new ArgumentNullException(nameof(htmlFactsService));
}
_htmlFactsService = htmlFactsService;
_htmlFactsService = htmlFactsService ?? throw new ArgumentNullException(nameof(htmlFactsService));
}
public override IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
public ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
@ -59,12 +55,12 @@ internal class MarkupTransitionCompletionItemProvider : RazorCompletionItemProvi
if (owner is null)
{
Debug.Fail("Owner should never be null.");
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
if (!AtMarkupTransitionCompletionPoint(owner))
{
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var parent = owner.Parent;
@ -74,11 +70,10 @@ internal class MarkupTransitionCompletionItemProvider : RazorCompletionItemProvi
if (!_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out _) ||
!containingTagNameToken.Span.IntersectsWith(context.AbsoluteIndex))
{
return Array.Empty<RazorCompletionItem>();
return ImmutableArray<RazorCompletionItem>.Empty;
}
var completions = new List<RazorCompletionItem>() { MarkupTransitionCompletionItem };
return completions;
return ImmutableArray.Create(MarkupTransitionCompletionItem);
}
private static bool AtMarkupTransitionCompletionPoint(RazorSyntaxNode owner)

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

@ -1,11 +1,51 @@
// 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.Collections.Immutable;
using System.Composition;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.CodeAnalysis.Razor.Completion;
internal abstract class RazorCompletionFactsService
[Shared]
[Export(typeof(IRazorCompletionFactsService))]
internal class RazorCompletionFactsService : IRazorCompletionFactsService
{
public abstract IReadOnlyList<RazorCompletionItem> GetCompletionItems(RazorCompletionContext razorCompletionContext);
private readonly ImmutableArray<IRazorCompletionItemProvider> _providers;
[ImportingConstructor]
public RazorCompletionFactsService([ImportMany] IEnumerable<IRazorCompletionItemProvider> providers)
{
if (providers is null)
{
throw new ArgumentNullException(nameof(providers));
}
_providers = providers.ToImmutableArray();
}
public ImmutableArray<RazorCompletionItem> GetCompletionItems(RazorCompletionContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.TagHelperDocumentContext is null)
{
throw new ArgumentNullException(nameof(context.TagHelperDocumentContext));
}
using var completions = new PooledArrayBuilder<RazorCompletionItem>();
foreach (var provider in _providers)
{
var items = provider.GetCompletionItems(context);
completions.AddRange(items);
}
return completions.DrainToImmutable();
}
}

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

@ -0,0 +1,42 @@
// 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 Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
namespace Microsoft.VisualStudio.Editor.Razor.Completion;
/// <summary>
/// Compares <see cref="CompletionItem"/>s by display text using the current culture.
/// </summary>
internal sealed class CompletionItemDisplayTextComparer : IComparer<CompletionItem>
{
public static readonly CompletionItemDisplayTextComparer Instance = new();
private CompletionItemDisplayTextComparer()
{
}
public int Compare(CompletionItem x, CompletionItem y)
{
var displayText1 = x?.DisplayText;
var displayText2 = y?.DisplayText;
if (displayText1 is null)
{
if (displayText2 is not null)
{
return -1;
}
return 0;
}
else if (displayText2 is null)
{
return 1;
}
return StringComparer.CurrentCulture.Compare(displayText1, displayText2);
}
}

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

@ -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);
}

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

@ -4,11 +4,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Tooltip;
@ -38,45 +38,25 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
}.ToImmutableArray();
private readonly VisualStudioRazorParser _parser;
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly ICompletionBroker _completionBroker;
private readonly VisualStudioDescriptionFactory _descriptionFactory;
private readonly IVisualStudioDescriptionFactory _descriptionFactory;
private readonly JoinableTaskFactory _joinableTaskFactory;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
public RazorDirectiveAttributeCompletionSource(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
VisualStudioRazorParser parser,
RazorCompletionFactsService completionFactsService,
IRazorCompletionFactsService completionFactsService,
ICompletionBroker completionBroker,
VisualStudioDescriptionFactory descriptionFactory,
IVisualStudioDescriptionFactory descriptionFactory,
JoinableTaskFactory joinableTaskFactory)
{
if (projectSnapshotManagerDispatcher is null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
}
if (parser is null)
{
throw new ArgumentNullException(nameof(parser));
}
if (completionFactsService is null)
{
throw new ArgumentNullException(nameof(completionFactsService));
}
if (descriptionFactory is null)
{
throw new ArgumentNullException(nameof(descriptionFactory));
}
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_parser = parser;
_completionFactsService = completionFactsService;
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
_parser = parser ?? throw new ArgumentNullException(nameof(parser));
_completionFactsService = completionFactsService ?? throw new ArgumentNullException(nameof(completionFactsService));
_completionBroker = completionBroker;
_descriptionFactory = descriptionFactory;
_descriptionFactory = descriptionFactory ?? throw new ArgumentNullException(nameof(descriptionFactory));
_joinableTaskFactory = joinableTaskFactory;
}
@ -99,7 +79,7 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
var razorCompletionContext = new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperDocumentContext);
var razorCompletionItems = _completionFactsService.GetCompletionItems(razorCompletionContext);
if (razorCompletionItems.Count == 0)
if (razorCompletionItems.Length == 0)
{
return CompletionContext.Empty;
}
@ -121,8 +101,9 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
activeSession.Dismiss();
}
var completionItems = new List<CompletionItem>();
using var _ = ArrayBuilderPool<CompletionItem>.GetPooledObject(out var completionItems);
var completionItemKinds = new HashSet<RazorCompletionItemKind>();
foreach (var razorCompletionItem in razorCompletionItems)
{
if (razorCompletionItem.Kind != RazorCompletionItemKind.DirectiveAttribute &&
@ -150,9 +131,10 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
}
session.Properties.SetCompletionItemKinds(completionItemKinds);
var orderedCompletionItems = completionItems.OrderBy(item => item.DisplayText);
var context = new CompletionContext(orderedCompletionItems.ToImmutableArray());
return context;
completionItems.Sort(CompletionItemDisplayTextComparer.Instance);
return new CompletionContext(completionItems.ToImmutable());
}
catch (OperationCanceledException)
{

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

@ -23,50 +23,24 @@ namespace Microsoft.VisualStudio.Editor.Razor.Completion;
internal class RazorDirectiveAttributeCompletionSourceProvider : IAsyncCompletionSourceProvider
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly ICompletionBroker _completionBroker;
private readonly VisualStudioDescriptionFactory _descriptionFactory;
private readonly IVisualStudioDescriptionFactory _descriptionFactory;
private readonly JoinableTaskContext _joinableTaskContext;
[ImportingConstructor]
public RazorDirectiveAttributeCompletionSourceProvider(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
RazorCompletionFactsService completionFactsService,
IAsyncCompletionBroker asyncCoompletionBroker,
IRazorCompletionFactsService completionFactsService,
ICompletionBroker completionBroker,
VisualStudioDescriptionFactory descriptionFactory,
IVisualStudioDescriptionFactory descriptionFactory,
JoinableTaskContext joinableTaskContext)
{
if (projectSnapshotManagerDispatcher is null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
}
if (completionFactsService is null)
{
throw new ArgumentNullException(nameof(completionFactsService));
}
if (asyncCoompletionBroker is null)
{
throw new ArgumentNullException(nameof(asyncCoompletionBroker));
}
if (descriptionFactory is null)
{
throw new ArgumentNullException(nameof(descriptionFactory));
}
if (joinableTaskContext is null)
{
throw new ArgumentNullException(nameof(joinableTaskContext));
}
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_completionFactsService = completionFactsService;
_completionBroker = completionBroker;
_descriptionFactory = descriptionFactory;
_joinableTaskContext = joinableTaskContext;
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
_completionFactsService = completionFactsService ?? throw new ArgumentNullException(nameof(completionFactsService));
_completionBroker = completionBroker ?? throw new ArgumentNullException(nameof(completionBroker));
_descriptionFactory = descriptionFactory ?? throw new ArgumentNullException(nameof(descriptionFactory));
_joinableTaskContext = joinableTaskContext ?? throw new ArgumentNullException(nameof(joinableTaskContext));
}
public IAsyncCompletionSource? GetOrCreate(ITextView textView)
@ -96,13 +70,12 @@ internal class RazorDirectiveAttributeCompletionSourceProvider : IAsyncCompletio
return null;
}
var completionSource = new RazorDirectiveAttributeCompletionSource(
return new RazorDirectiveAttributeCompletionSource(
_projectSnapshotManagerDispatcher,
parser,
_completionFactsService,
_completionBroker,
_descriptionFactory,
_joinableTaskContext.Factory);
return completionSource;
}
}

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

@ -2,12 +2,12 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.Core.Imaging;
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
@ -33,11 +33,11 @@ internal class RazorDirectiveCompletionSource : IAsyncCompletionSource
// Internal for testing
internal readonly VisualStudioRazorParser Parser;
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
public RazorDirectiveCompletionSource(
VisualStudioRazorParser parser,
RazorCompletionFactsService completionFactsService)
IRazorCompletionFactsService completionFactsService)
{
if (parser is null)
{
@ -77,7 +77,8 @@ internal class RazorDirectiveCompletionSource : IAsyncCompletionSource
var razorCompletionContext = new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperDocumentContext);
var razorCompletionItems = _completionFactsService.GetCompletionItems(razorCompletionContext);
var completionItems = new List<CompletionItem>();
using var _ = ArrayBuilderPool<CompletionItem>.GetPooledObject(out var completionItems);
foreach (var razorCompletionItem in razorCompletionItems)
{
if (razorCompletionItem.Kind != RazorCompletionItemKind.Directive)
@ -96,13 +97,13 @@ internal class RazorDirectiveCompletionSource : IAsyncCompletionSource
suffix: string.Empty,
sortText: razorCompletionItem.DisplayText,
attributeIcons: ImmutableArray<ImageElement>.Empty);
var completionDescription = razorCompletionItem.GetDirectiveCompletionDescription();
completionItem.Properties.AddProperty(DescriptionKey, completionDescription);
completionItems.Add(completionItem);
}
var context = new CompletionContext(completionItems.ToImmutableArray());
return context;
return new CompletionContext(completionItems.ToImmutable());
}
catch (OperationCanceledException)
{

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

@ -20,10 +20,10 @@ namespace Microsoft.VisualStudio.Editor.Razor.Completion;
[ContentType(RazorConstants.LegacyCoreContentType)]
internal class RazorDirectiveCompletionSourceProvider : IAsyncCompletionSourceProvider
{
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
[ImportingConstructor]
public RazorDirectiveCompletionSourceProvider(RazorCompletionFactsService completionFactsService)
public RazorDirectiveCompletionSourceProvider(IRazorCompletionFactsService completionFactsService)
{
if (completionFactsService is null)
{

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

@ -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";
}
}

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

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
public class LegacyRazorCompletionEndpointTest : LanguageServerTestBase
{
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly CompletionListCache _completionListCache;
private readonly VSInternalClientCapabilities _clientCapabilities;
@ -36,7 +36,7 @@ public class LegacyRazorCompletionEndpointTest : LanguageServerTestBase
var tagHelperFactsService = new DefaultTagHelperFactsService();
var tagHelperCompletionService = new LanguageServerTagHelperCompletionService(tagHelperFactsService);
var completionProviders = new RazorCompletionItemProvider[]
var completionProviders = new IRazorCompletionItemProvider[]
{
new DirectiveCompletionItemProvider(),
new DirectiveAttributeCompletionItemProvider(tagHelperFactsService),
@ -44,7 +44,7 @@ public class LegacyRazorCompletionEndpointTest : LanguageServerTestBase
new TagHelperCompletionProvider(tagHelperCompletionService, new DefaultHtmlFactsService(), tagHelperFactsService, TestRazorLSPOptionsMonitor.Create())
};
_completionFactsService = new DefaultRazorCompletionFactsService(completionProviders);
_completionFactsService = new RazorCompletionFactsService(completionProviders);
_completionListCache = new CompletionListCache();
_clientCapabilities = new VSInternalClientCapabilities()
{

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

@ -4,7 +4,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -27,7 +27,6 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
private readonly CompletionListCache _completionListCache;
private readonly VSInternalCompletionSetting _completionCapability;
private readonly VSInternalClientCapabilities _defaultClientCapability;
private readonly VSInternalClientCapabilities _vsClientCapability;
public LegacyRazorCompletionResolveEndpointTest(ITestOutputHelper testOutput)
: base(testOutput)
@ -50,15 +49,6 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
Completion = _completionCapability,
},
};
_vsClientCapability = new VSInternalClientCapabilities()
{
TextDocument = new TextDocumentClientCapabilities()
{
Completion = _completionCapability,
},
SupportsVisualStudioExtensions = true,
};
}
[Fact]
@ -69,7 +59,7 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
endpoint.ApplyCapabilities(new(), _defaultClientCapability);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.Directive);
razorCompletionItem.SetDirectiveCompletionDescription(new DirectiveCompletionDescription("Test directive"));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single();
var parameters = ConvertToBridgedItem(completionItem);
var requestContext = CreateRazorRequestContext(documentContext: null);
@ -89,7 +79,7 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
endpoint.ApplyCapabilities(new(), _defaultClientCapability);
var razorCompletionItem = new RazorCompletionItem("@...", "@", RazorCompletionItemKind.MarkupTransition);
razorCompletionItem.SetMarkupTransitionCompletionDescription(new MarkupTransitionCompletionDescription("Test description"));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single();
var parameters = ConvertToBridgedItem(completionItem);
var requestContext = CreateRazorRequestContext(documentContext: null);
@ -117,7 +107,7 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
endpoint.ApplyCapabilities(new(), _defaultClientCapability);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttribute);
razorCompletionItem.SetAttributeCompletionDescription(new AggregateBoundAttributeDescription(Array.Empty<BoundAttributeDescriptionInfo>()));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single();
var parameters = ConvertToBridgedItem(completionItem);
var requestContext = CreateRazorRequestContext(documentContext: null);
@ -145,7 +135,7 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
endpoint.ApplyCapabilities(new(), _defaultClientCapability);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttributeParameter);
razorCompletionItem.SetAttributeCompletionDescription(new AggregateBoundAttributeDescription(Array.Empty<BoundAttributeDescriptionInfo>()));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single();
var parameters = ConvertToBridgedItem(completionItem);
var requestContext = CreateRazorRequestContext(documentContext: null);
@ -173,7 +163,7 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
endpoint.ApplyCapabilities(new(), _defaultClientCapability);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperElement);
razorCompletionItem.SetTagHelperElementDescriptionInfo(new AggregateBoundElementDescription(Array.Empty<BoundElementDescriptionInfo>()));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single();
var parameters = ConvertToBridgedItem(completionItem);
var requestContext = CreateRazorRequestContext(documentContext: null);
@ -201,7 +191,7 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
endpoint.ApplyCapabilities(new(), _defaultClientCapability);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperAttribute);
razorCompletionItem.SetAttributeCompletionDescription(new AggregateBoundAttributeDescription(Array.Empty<BoundAttributeDescriptionInfo>()));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single();
var parameters = ConvertToBridgedItem(completionItem);
var requestContext = CreateRazorRequestContext(documentContext: null);
@ -238,9 +228,9 @@ public class LegacyRazorCompletionResolveEndpointTest : LanguageServerTestBase
Assert.Null(newCompletionItem.Documentation);
}
private VSInternalCompletionList CreateLSPCompletionList(IReadOnlyList<RazorCompletionItem> razorCompletionItems)
private VSInternalCompletionList CreateLSPCompletionList(params RazorCompletionItem[] razorCompletionItems)
{
var completionList = LegacyRazorCompletionEndpoint.CreateLSPCompletionList(razorCompletionItems, _defaultClientCapability);
var completionList = LegacyRazorCompletionEndpoint.CreateLSPCompletionList(razorCompletionItems.ToImmutableArray(), _defaultClientCapability);
var resultId = _completionListCache.Add(completionList, razorCompletionItems);
completionList.SetResultId(resultId, completionSetting: null);
return completionList;

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

@ -4,7 +4,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Tooltip;
@ -70,7 +70,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.Directive);
razorCompletionItem.SetDirectiveCompletionDescription(new DirectiveCompletionDescription("Test directive"));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -88,7 +88,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("@...", "@", RazorCompletionItemKind.MarkupTransition);
razorCompletionItem.SetMarkupTransitionCompletionDescription(new MarkupTransitionCompletionDescription("Test description"));
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -106,7 +106,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttribute);
razorCompletionItem.SetAttributeCompletionDescription(_attributeDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -124,7 +124,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttributeParameter);
razorCompletionItem.SetAttributeCompletionDescription(_attributeDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -142,7 +142,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperElement);
razorCompletionItem.SetTagHelperElementDescriptionInfo(_elementDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -160,7 +160,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperAttribute);
razorCompletionItem.SetAttributeCompletionDescription(_attributeDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -178,7 +178,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttribute);
razorCompletionItem.SetAttributeCompletionDescription(_attributeDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -196,7 +196,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttributeParameter);
razorCompletionItem.SetAttributeCompletionDescription(_attributeDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -214,7 +214,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperElement);
razorCompletionItem.SetTagHelperElementDescriptionInfo(_elementDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -232,7 +232,7 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
var resolver = new RazorCompletionItemResolver(_lspTagHelperTooltipFactory, _vsLspTagHelperTooltipFactory);
var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperAttribute);
razorCompletionItem.SetAttributeCompletionDescription(_attributeDescription);
var completionList = CreateLSPCompletionList(new[] { razorCompletionItem });
var completionList = CreateLSPCompletionList(razorCompletionItem);
var completionItem = completionList.Items.Single() as VSInternalCompletionItem;
// Act
@ -259,9 +259,6 @@ public class RazorCompletionItemResolverTest : LanguageServerTestBase
Assert.Null(resolvedCompletionItem);
}
private VSInternalCompletionList CreateLSPCompletionList(IReadOnlyList<RazorCompletionItem> razorCompletionItems)
{
var completionList = RazorCompletionListProvider.CreateLSPCompletionList(razorCompletionItems, _defaultClientCapability);
return completionList;
}
private VSInternalCompletionList CreateLSPCompletionList(params RazorCompletionItem[] razorCompletionItems)
=> RazorCompletionListProvider.CreateLSPCompletionList(razorCompletionItems.ToImmutableArray(), _defaultClientCapability);
}

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

@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
public class RazorCompletionListProvierTest : LanguageServerTestBase
{
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
private readonly CompletionListCache _completionListCache;
private readonly VSInternalClientCapabilities _clientCapabilities;
private readonly VSInternalCompletionContext _defaultCompletionContext;
@ -35,7 +35,7 @@ public class RazorCompletionListProvierTest : LanguageServerTestBase
public RazorCompletionListProvierTest(ITestOutputHelper testOutput)
: base(testOutput)
{
_completionFactsService = new DefaultRazorCompletionFactsService(GetCompletionProviders());
_completionFactsService = new RazorCompletionFactsService(GetCompletionProviders());
_completionListCache = new CompletionListCache();
_clientCapabilities = new VSInternalClientCapabilities()
{
@ -59,7 +59,7 @@ public class RazorCompletionListProvierTest : LanguageServerTestBase
_defaultCompletionContext = new VSInternalCompletionContext();
}
private static IEnumerable<RazorCompletionItemProvider> GetCompletionProviders(IOptionsMonitor<RazorLSPOptions> optionsMonitor = null)
private static IEnumerable<IRazorCompletionItemProvider> GetCompletionProviders(IOptionsMonitor<RazorLSPOptions> optionsMonitor = null)
{
// Working around strong naming restriction.
var tagHelperFactsService = new DefaultTagHelperFactsService();
@ -67,7 +67,7 @@ public class RazorCompletionListProvierTest : LanguageServerTestBase
optionsMonitor ??= TestRazorLSPOptionsMonitor.Create();
var completionProviders = new RazorCompletionItemProvider[]
var completionProviders = new IRazorCompletionItemProvider[]
{
new DirectiveCompletionItemProvider(),
new DirectiveAttributeCompletionItemProvider(tagHelperFactsService),
@ -582,7 +582,7 @@ public class RazorCompletionListProvierTest : LanguageServerTestBase
var optionsMonitor = TestRazorLSPOptionsMonitor.Create();
await optionsMonitor.UpdateAsync(optionsMonitor.CurrentValue with { AutoInsertAttributeQuotes = false }, DisposalToken);
var completionFactsService = new DefaultRazorCompletionFactsService(GetCompletionProviders(optionsMonitor));
var completionFactsService = new RazorCompletionFactsService(GetCompletionProviders(optionsMonitor));
var provider = new RazorCompletionListProvider(completionFactsService, _completionListCache, LoggerFactory);
// Act

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

@ -3,6 +3,7 @@
#nullable disable
using System.Collections.Immutable;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Test.Common;
@ -12,13 +13,8 @@ using Xunit.Abstractions;
namespace Microsoft.CodeAnalysis.Razor.Completion;
public class DefaultRazorCompletionFactsServiceTest : TestBase
public class DefaultRazorCompletionFactsServiceTest(ITestOutputHelper testOutput) : TestBase(testOutput)
{
public DefaultRazorCompletionFactsServiceTest(ITestOutputHelper testOutput)
: base(testOutput)
{
}
[Fact]
public void GetDirectiveCompletionItems_AllProvidersCompletionItems()
{
@ -27,10 +23,10 @@ public class DefaultRazorCompletionFactsServiceTest : TestBase
var tagHelperDocumentContext = TagHelperDocumentContext.Create(prefix: null, Enumerable.Empty<TagHelperDescriptor>());
var completionItem1 = new RazorCompletionItem("displayText1", "insertText1", RazorCompletionItemKind.Directive);
var context = new RazorCompletionContext(0, null, syntaxTree, tagHelperDocumentContext);
var provider1 = Mock.Of<RazorCompletionItemProvider>(p => p.GetCompletionItems(context) == new[] { completionItem1 }, MockBehavior.Strict);
var provider1 = Mock.Of<IRazorCompletionItemProvider>(p => p.GetCompletionItems(context) == ImmutableArray.Create(completionItem1), MockBehavior.Strict);
var completionItem2 = new RazorCompletionItem("displayText2", "insertText2", RazorCompletionItemKind.Directive);
var provider2 = Mock.Of<RazorCompletionItemProvider>(p => p.GetCompletionItems(context) == new[] { completionItem2 }, MockBehavior.Strict);
var completionFactsService = new DefaultRazorCompletionFactsService(new[] { provider1, provider2 });
var provider2 = Mock.Of<IRazorCompletionItemProvider>(p => p.GetCompletionItems(context) == ImmutableArray.Create(completionItem2), MockBehavior.Strict);
var completionFactsService = new RazorCompletionFactsService(new[] { provider1, provider2 });
// Act
var completionItems = completionFactsService.GetCompletionItems(context);

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

@ -93,7 +93,7 @@ public class DirectiveAttributeParameterCompletionItemProviderTest : RazorIntegr
var completions = _provider.GetCompletionItems(context);
// Assert
Assert.Equal(6, completions.Count);
Assert.Equal(6, completions.Length);
AssertContains(completions, "culture");
AssertContains(completions, "event");
AssertContains(completions, "format");

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

@ -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,11 +46,11 @@ 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),
Mock.Of<RazorCompletionFactsService>(MockBehavior.Strict),
Mock.Of<IRazorCompletionFactsService>(MockBehavior.Strict),
Mock.Of<ICompletionBroker>(MockBehavior.Strict),
descriptionFactory,
JoinableTaskFactory);
@ -240,9 +240,9 @@ public class RazorDirectiveAttributeCompletionSourceTest : ProjectSnapshotManage
var source = new RazorDirectiveAttributeCompletionSource(
Dispatcher,
Mock.Of<VisualStudioRazorParser>(MockBehavior.Strict),
Mock.Of<RazorCompletionFactsService>(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;
}

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

@ -21,7 +21,7 @@ public class RazorDirectiveCompletionSourceProviderTest : ProjectSnapshotManager
{
private readonly IContentType _razorContentType;
private readonly IContentType _nonRazorContentType;
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
public RazorDirectiveCompletionSourceProviderTest(ITestOutputHelper testOutput)
: base(testOutput)
@ -34,7 +34,7 @@ public class RazorDirectiveCompletionSourceProviderTest : ProjectSnapshotManager
c => c.IsOfType(It.IsAny<string>()) == false,
MockBehavior.Strict);
_completionFactsService = Mock.Of<RazorCompletionFactsService>(MockBehavior.Strict);
_completionFactsService = Mock.Of<IRazorCompletionFactsService>(MockBehavior.Strict);
}
[Fact]

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

@ -33,12 +33,12 @@ public class RazorDirectiveCompletionSourceTest : ProjectSnapshotManagerDispatch
CSharpCodeParser.TagHelperPrefixDirectiveDescriptor,
};
private readonly RazorCompletionFactsService _completionFactsService;
private readonly IRazorCompletionFactsService _completionFactsService;
public RazorDirectiveCompletionSourceTest(ITestOutputHelper testOutput)
: base(testOutput)
{
_completionFactsService = new DefaultRazorCompletionFactsService(new[] { new DirectiveCompletionItemProvider() });
_completionFactsService = new RazorCompletionFactsService(new[] { new DirectiveCompletionItemProvider() });
}
[UIFact]

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

@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace System.Collections.Generic;
internal static class EnumerableExtensions
{
public static ImmutableArray<TResult> SelectAsArray<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector)
{
if (source is IReadOnlyList<T> list)
{
return list.SelectAsArray(selector);
}
return BuildResult(source, selector);
static ImmutableArray<TResult> BuildResult(IEnumerable<T> items, Func<T, TResult> selector)
{
using var results = new PooledArrayBuilder<TResult>();
foreach (var item in items)
{
results.Add(selector(item));
}
return results.DrainToImmutable();
}
}
}

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

@ -73,15 +73,14 @@ internal static class ImmutableArrayExtensions
static ImmutableArray<TResult> BuildResult(ImmutableArray<T> items, Func<T, TResult> selector)
{
using var _ = ArrayBuilderPool<TResult>.GetPooledObject(out var result);
result.SetCapacityIfLarger(items.Length);
using var results = new PooledArrayBuilder<TResult>(capacity: items.Length);
foreach (var item in items)
{
result.Add(selector(item));
results.Add(selector(item));
}
return result.DrainToImmutable();
return results.DrainToImmutable();
}
}
}

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

@ -67,6 +67,17 @@ internal ref struct PooledArrayBuilder<T>
_builder.Add(item);
}
public void AddRange(ImmutableArray<T> items)
{
if (items.Length == 0)
{
return;
}
_builder ??= GetBuilder();
_builder.AddRange(items);
}
public void AddRange(IReadOnlyList<T> items)
{
if (items.Count == 0)

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

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace System.Collections.Generic;
internal static class ReadOnlyListExtensions
{
public static ImmutableArray<TResult> SelectAsArray<T, TResult>(this IReadOnlyList<T> source, Func<T, TResult> selector)
{
return source switch
{
[] => ImmutableArray<TResult>.Empty,
[var item] => ImmutableArray.Create(selector(item)),
[var item1, var item2] => ImmutableArray.Create(selector(item1), selector(item2)),
[var item1, var item2, var item3] => ImmutableArray.Create(selector(item1), selector(item2), selector(item3)),
[var item1, var item2, var item3, var item4] => ImmutableArray.Create(selector(item1), selector(item2), selector(item3), selector(item4)),
var items => BuildResult(items, selector)
};
static ImmutableArray<TResult> BuildResult(IReadOnlyList<T> items, Func<T, TResult> selector)
{
using var results = new PooledArrayBuilder<TResult>(capacity: items.Count);
for (var i = 0; i < items.Count; i++)
{
results.Add(selector(items[i]));
}
return results.DrainToImmutable();
}
}
}