Allow catch-all THDs to apply to all completions.

- Prior to this catch-alls were explicitly ignored not to populate the IntelliSense with an excess amount of `TagHelper`s. To maintain backwards compatible expectations this commit reverts that expectation.
- Scoped an existing test to only test its primary scenario.
- Added a new test to verify that catch-alls accurately apply to all completions (including dynamically added ones).

#1219
This commit is contained in:
N. Taylor Mullen 2017-04-13 16:11:13 -07:00
parent cd486226d6
commit 46a5ca972f
2 changed files with 65 additions and 18 deletions

View File

@ -44,18 +44,20 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
_ => new HashSet<TagHelperDescriptor>(),
StringComparer.OrdinalIgnoreCase);
var catchAllDescriptors = new HashSet<TagHelperDescriptor>();
var possibleChildDescriptors = _tagHelperFactsService.GetTagHelpersGivenParent(completionContext.DocumentContext, completionContext.ContainingTagName);
foreach (var possibleDescriptor in possibleChildDescriptors)
{
var addRuleCompletions = false;
var outputHint = possibleDescriptor.TagOutputHint;
// Filter out catch-all rules because TagHelpers that target attributes only would light up every child tag otherwise. Force those TagHelpers
// to have additional requirements before showing them in the element completion list.
var nonCatchAllRules = possibleDescriptor.TagMatchingRules.Where(rule => rule.TagName != TagHelperMatchingConventions.ElementCatchAllName);
foreach (var rule in nonCatchAllRules)
foreach (var rule in possibleDescriptor.TagMatchingRules)
{
if (elementCompletions.ContainsKey(rule.TagName))
if (rule.TagName == TagHelperMatchingConventions.ElementCatchAllName)
{
catchAllDescriptors.Add(possibleDescriptor);
}
else if (elementCompletions.ContainsKey(rule.TagName))
{
addRuleCompletions = true;
}
@ -74,19 +76,35 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
if (addRuleCompletions)
{
if (!elementCompletions.TryGetValue(rule.TagName, out var existingRuleDescriptors))
{
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
elementCompletions[rule.TagName] = existingRuleDescriptors;
}
existingRuleDescriptors.Add(possibleDescriptor);
UpdateCompletions(rule.TagName, possibleDescriptor);
}
}
}
// We needed to track all catch-alls and update their completions after all other completions have been completed.
// This way, any TagHelper added completions will also have catch-alls listed under their entries.
foreach (var catchAllDescriptor in catchAllDescriptors)
{
foreach (var completionTagNames in elementCompletions.Keys)
{
UpdateCompletions(completionTagNames, catchAllDescriptor);
}
}
var result = ElementCompletionResult.Create(elementCompletions);
return result;
void UpdateCompletions(string tagName, TagHelperDescriptor possibleDescriptor)
{
if (!elementCompletions.TryGetValue(tagName, out var existingRuleDescriptors))
{
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
elementCompletions[tagName] = existingRuleDescriptors;
}
existingRuleDescriptors.Add(possibleDescriptor);
}
}
private void AddAllowedChildrenCompletions(

View File

@ -10,6 +10,39 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
{
public class DefaultTagHelperCompletionServiceTest
{
[Fact]
public void GetElementCompletions_CatchAllsApplyToAllCompletions()
{
// Arrange
var documentDescriptors = new[]
{
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
.TagMatchingRule(rule => rule.RequireTagName("superli"))
.Build(),
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
.TagMatchingRule(rule => rule.RequireTagName("*"))
.Build(),
};
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
{
["superli"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
});
var existingCompletions = new[] { "li" };
var completionContext = BuildCompletionContext(
documentDescriptors,
existingCompletions,
containingTagName: "ul");
var service = CreateTagHelperCompletionFactsService();
// Act
var completions = service.GetElementCompletions(completionContext);
// Assert
AssertCompletionsAreEquivalent(expectedCompletions, completions);
}
[Fact]
public void GetElementCompletions_AllowsMultiTargetingTagHelpers()
{
@ -183,13 +216,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
.AllowChildTag("div")
.Build(),
};
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
{
["p"] = new HashSet<TagHelperDescriptor>(),
["em"] = new HashSet<TagHelperDescriptor>(),
});
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>());
var existingCompletions = new[] { "p", "em" };
var existingCompletions = Enumerable.Empty<string>();
var completionContext = BuildCompletionContext(
documentDescriptors,
existingCompletions,