From 4eea2f6992d9e19524e786c1261bb28dde53ee38 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Thu, 1 Feb 2018 15:46:26 -0800 Subject: [PATCH] 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 --- .../DefaultTagHelperFactsServiceInternal.cs | 163 ------------------ ...ultTagHelperFactsServiceInternalFactory.cs | 17 -- .../TagHelperFactsServiceInternal.cs | 20 --- .../DefaultTagHelperCompletionService.cs | 14 +- .../DefaultTagHelperFactsService.cs | 138 +++++++++++++-- .../DefaultTagHelperCompletionServiceTest.cs | 2 +- .../DefaultTagHelperFactsServiceTest.cs} | 30 ++-- 7 files changed, 146 insertions(+), 238 deletions(-) delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs rename test/{Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs => Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperFactsServiceTest.cs} (94%) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs deleted file mode 100644 index 99cf4f6d12..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternal.cs +++ /dev/null @@ -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> 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 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(); - 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 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(); - 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 GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag) - { - if (documentContext == null) - { - throw new ArgumentNullException(nameof(documentContext)); - } - - var matchingDescriptors = new List(); - 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; - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs deleted file mode 100644 index 4e89fcdff1..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperFactsServiceInternalFactory.cs +++ /dev/null @@ -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(); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs deleted file mode 100644 index 78d26cfdbb..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperFactsServiceInternal.cs +++ /dev/null @@ -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> attributes, string parentTag, bool parentIsTagHelper); - - public abstract IEnumerable GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding); - - public abstract IReadOnlyList GetTagHelpersGivenTag(TagHelperDocumentContext documentContext, string tagName, string parentTag); - - public abstract IReadOnlyList GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag); - } -} diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs index 0bb688e1b3..7b927f48ad 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperCompletionService.cs @@ -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 _emptyHashSet = new HashSet(); [ImportingConstructor] - public DefaultTagHelperCompletionService(VisualStudioWorkspaceAccessor workspaceAccessor) + public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService) { - var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name); - _tagHelperFactsService = razorLanguageServices.GetRequiredService(); - } + if (tagHelperFactsService == null) + { + throw new ArgumentNullException(nameof(tagHelperFactsService)); + } - // Internal for testing - internal DefaultTagHelperCompletionService(TagHelperFactsServiceInternal tagHelperFactsService) - { _tagHelperFactsService = tagHelperFactsService; } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs index 2769d59657..1d23ce0231 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultTagHelperFactsService.cs @@ -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(); - } - 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 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(); + 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 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(); + 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 GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag) { - return _tagHelperFactsService.GetTagHelpersGivenParent(documentContext, parentTag); + if (documentContext == null) + { + throw new ArgumentNullException(nameof(documentContext)); + } + + var matchingDescriptors = new List(); + 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; } } } diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs index 1422e44815..aca20bcb3d 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperCompletionServiceTest.cs @@ -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; diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperFactsServiceTest.cs similarity index 94% rename from test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs rename to test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperFactsServiceTest.cs index 5001398a36..5d2d12452d 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperFactsServiceInternalTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultTagHelperFactsServiceTest.cs @@ -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>(), 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("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>(), 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>(), 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");