Change DefaultTagHelperFactsService to not expect a primary workspace.

- Removed `TagHelperFactsServiceInternal` since we had no way of retrieving the internal Workspace service given the exposed public API. Not to mention I think the `TagHelperFactsService` made more sense as a MEF service anyhow.
- Moved `TagHelperFactsServiceInternal` tests to `Microsoft.VisualStudio.Editor.Razor.Test` project and changed them to utilize the non-"internal" version.
- Updated completion service API to rely on non-`TagHelperFactsServiceInternal` pieces.

#2004
This commit is contained in:
N. Taylor Mullen 2018-02-01 15:46:26 -08:00
parent 64f9474eb1
commit 4eea2f6992
7 changed files with 146 additions and 238 deletions

View File

@ -1,163 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.CodeAnalysis.Razor
{
internal class DefaultTagHelperFactsServiceInternal : TagHelperFactsServiceInternal
{
public override TagHelperBinding GetTagHelperBinding(
TagHelperDocumentContext documentContext,
string tagName,
IEnumerable<KeyValuePair<string, string>> attributes,
string parentTag,
bool parentIsTagHelper)
{
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
if (tagName == null)
{
throw new ArgumentNullException(nameof(tagName));
}
if (attributes == null)
{
throw new ArgumentNullException(nameof(attributes));
}
var descriptors = documentContext.TagHelpers;
if (descriptors == null || descriptors.Count == 0)
{
return null;
}
var prefix = documentContext.Prefix;
var tagHelperBinder = new TagHelperBinder(prefix, descriptors);
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag, parentIsTagHelper);
return binding;
}
public override IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(
TagHelperDocumentContext documentContext,
string attributeName,
TagHelperBinding binding)
{
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
if (attributeName == null)
{
throw new ArgumentNullException(nameof(attributeName));
}
if (binding == null)
{
throw new ArgumentNullException(nameof(binding));
}
var matchingBoundAttributes = new List<BoundAttributeDescriptor>();
foreach (var descriptor in binding.Descriptors)
{
foreach (var boundAttributeDescriptor in descriptor.BoundAttributes)
{
if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, boundAttributeDescriptor))
{
matchingBoundAttributes.Add(boundAttributeDescriptor);
// Only one bound attribute can match an attribute
break;
}
}
}
return matchingBoundAttributes;
}
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(
TagHelperDocumentContext documentContext,
string tagName,
string parentTag)
{
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
if (tagName == null)
{
throw new ArgumentNullException(nameof(tagName));
}
var matchingDescriptors = new List<TagHelperDescriptor>();
var descriptors = documentContext?.TagHelpers;
if (descriptors?.Count == 0)
{
return matchingDescriptors;
}
var prefix = documentContext.Prefix ?? string.Empty;
if (!tagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
// Can't possibly match TagHelpers, it doesn't start with the TagHelperPrefix.
return matchingDescriptors;
}
var tagNameWithoutPrefix = tagName.Substring(prefix.Length);
for (var i = 0; i < descriptors.Count; i++)
{
var descriptor = descriptors[i];
foreach (var rule in descriptor.TagMatchingRules)
{
if (TagHelperMatchingConventions.SatisfiesTagName(tagNameWithoutPrefix, rule) &&
TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
{
matchingDescriptors.Add(descriptor);
break;
}
}
}
return matchingDescriptors;
}
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag)
{
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
var matchingDescriptors = new List<TagHelperDescriptor>();
var descriptors = documentContext?.TagHelpers;
if (descriptors?.Count == 0)
{
return matchingDescriptors;
}
for (var i = 0; i < descriptors.Count; i++)
{
var descriptor = descriptors[i];
foreach (var rule in descriptor.TagMatchingRules)
{
if (TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
{
matchingDescriptors.Add(descriptor);
break;
}
}
}
return matchingDescriptors;
}
}
}

View File

@ -1,17 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Razor
{
[ExportLanguageServiceFactory(typeof(TagHelperFactsServiceInternal), RazorLanguage.Name, ServiceLayer.Default)]
internal class DefaultTagHelperFactsServiceInternalFactory : ILanguageServiceFactory
{
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
{
return new DefaultTagHelperFactsServiceInternal();
}
}
}

View File

@ -1,20 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Razor
{
internal abstract class TagHelperFactsServiceInternal : ILanguageService
{
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag, bool parentIsTagHelper);
public abstract IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(TagHelperDocumentContext documentContext, string tagName, string parentTag);
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag);
}
}

View File

@ -15,19 +15,17 @@ namespace Microsoft.VisualStudio.Editor.Razor
[Export(typeof(TagHelperCompletionService))]
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
{
private readonly TagHelperFactsServiceInternal _tagHelperFactsService;
private readonly TagHelperFactsService _tagHelperFactsService;
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
[ImportingConstructor]
public DefaultTagHelperCompletionService(VisualStudioWorkspaceAccessor workspaceAccessor)
public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService)
{
var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
_tagHelperFactsService = razorLanguageServices.GetRequiredService<TagHelperFactsServiceInternal>();
}
if (tagHelperFactsService == null)
{
throw new ArgumentNullException(nameof(tagHelperFactsService));
}
// Internal for testing
internal DefaultTagHelperCompletionService(TagHelperFactsServiceInternal tagHelperFactsService)
{
_tagHelperFactsService = tagHelperFactsService;
}

View File

@ -4,8 +4,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.VisualStudio.Editor.Razor
{
@ -13,15 +13,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
[Export(typeof(TagHelperFactsService))]
internal class DefaultTagHelperFactsService : TagHelperFactsService
{
private readonly TagHelperFactsServiceInternal _tagHelperFactsService;
[ImportingConstructor]
public DefaultTagHelperFactsService(VisualStudioWorkspaceAccessor workspaceAccessor)
{
var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
_tagHelperFactsService = razorLanguageServices.GetRequiredService<TagHelperFactsServiceInternal>();
}
public override TagHelperBinding GetTagHelperBinding(
TagHelperDocumentContext documentContext,
string tagName,
@ -29,7 +20,32 @@ namespace Microsoft.VisualStudio.Editor.Razor
string parentTag,
bool parentIsTagHelper)
{
return _tagHelperFactsService.GetTagHelperBinding(documentContext, tagName, attributes, parentTag, parentIsTagHelper);
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
if (tagName == null)
{
throw new ArgumentNullException(nameof(tagName));
}
if (attributes == null)
{
throw new ArgumentNullException(nameof(attributes));
}
var descriptors = documentContext.TagHelpers;
if (descriptors == null || descriptors.Count == 0)
{
return null;
}
var prefix = documentContext.Prefix;
var tagHelperBinder = new TagHelperBinder(prefix, descriptors);
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag, parentIsTagHelper);
return binding;
}
public override IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(
@ -37,7 +53,37 @@ namespace Microsoft.VisualStudio.Editor.Razor
string attributeName,
TagHelperBinding binding)
{
return _tagHelperFactsService.GetBoundTagHelperAttributes(documentContext, attributeName, binding);
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
if (attributeName == null)
{
throw new ArgumentNullException(nameof(attributeName));
}
if (binding == null)
{
throw new ArgumentNullException(nameof(binding));
}
var matchingBoundAttributes = new List<BoundAttributeDescriptor>();
foreach (var descriptor in binding.Descriptors)
{
foreach (var boundAttributeDescriptor in descriptor.BoundAttributes)
{
if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, boundAttributeDescriptor))
{
matchingBoundAttributes.Add(boundAttributeDescriptor);
// Only one bound attribute can match an attribute
break;
}
}
}
return matchingBoundAttributes;
}
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(
@ -45,12 +91,76 @@ namespace Microsoft.VisualStudio.Editor.Razor
string tagName,
string parentTag)
{
return _tagHelperFactsService.GetTagHelpersGivenTag(documentContext, tagName, parentTag);
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
if (tagName == null)
{
throw new ArgumentNullException(nameof(tagName));
}
var matchingDescriptors = new List<TagHelperDescriptor>();
var descriptors = documentContext?.TagHelpers;
if (descriptors?.Count == 0)
{
return matchingDescriptors;
}
var prefix = documentContext.Prefix ?? string.Empty;
if (!tagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
// Can't possibly match TagHelpers, it doesn't start with the TagHelperPrefix.
return matchingDescriptors;
}
var tagNameWithoutPrefix = tagName.Substring(prefix.Length);
for (var i = 0; i < descriptors.Count; i++)
{
var descriptor = descriptors[i];
foreach (var rule in descriptor.TagMatchingRules)
{
if (TagHelperMatchingConventions.SatisfiesTagName(tagNameWithoutPrefix, rule) &&
TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
{
matchingDescriptors.Add(descriptor);
break;
}
}
}
return matchingDescriptors;
}
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag)
{
return _tagHelperFactsService.GetTagHelpersGivenParent(documentContext, parentTag);
if (documentContext == null)
{
throw new ArgumentNullException(nameof(documentContext));
}
var matchingDescriptors = new List<TagHelperDescriptor>();
var descriptors = documentContext?.TagHelpers;
if (descriptors?.Count == 0)
{
return matchingDescriptors;
}
for (var i = 0; i < descriptors.Count; i++)
{
var descriptor = descriptors[i];
foreach (var rule in descriptor.TagMatchingRules)
{
if (TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
{
matchingDescriptors.Add(descriptor);
break;
}
}
}
return matchingDescriptors;
}
}
}

View File

@ -995,7 +995,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService()
{
var tagHelperFactsService = new DefaultTagHelperFactsServiceInternal();
var tagHelperFactsService = new DefaultTagHelperFactsService();
var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactsService);
return completionFactService;

View File

@ -6,9 +6,9 @@ using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor
namespace Microsoft.VisualStudio.Editor.Razor
{
public class DefaultTagHelperFactsServiceInternalTest
public class DefaultTagHelperFactsServiceTest
{
// Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through
// into TagHelperDescriptorProvider.GetTagHelperBinding.
@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
@ -66,7 +66,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build(),
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
var attributes = new[]
{
new KeyValuePair<string, string>("asp-for", "Name")
@ -108,7 +108,7 @@ namespace Microsoft.CodeAnalysis.Razor
documentDescriptors[0].BoundAttributes.Last()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
// Act
@ -143,7 +143,7 @@ namespace Microsoft.CodeAnalysis.Razor
documentDescriptors[0].BoundAttributes.First()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
// Act
@ -164,7 +164,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null);
@ -184,7 +184,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p");
@ -217,7 +217,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div");
@ -244,7 +244,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div");
@ -277,7 +277,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div");
@ -297,7 +297,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
@ -322,7 +322,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenParent(documentContext, parentTag: null /* root */);
@ -343,7 +343,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenParent(documentContext, "p");
@ -376,7 +376,7 @@ namespace Microsoft.CodeAnalysis.Razor
.Build()
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsServiceInternal();
var service = new DefaultTagHelperFactsService();
// Act
var descriptors = service.GetTagHelpersGivenParent(documentContext, "div");